1 /* Copyright (C) 1991, 1994, 1997-1998, 2000, 2003-2020 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 <https://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 __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>
__libc_lock_define_initialized(static,envlock)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 #if defined _WIN32 && ! defined __CYGWIN__
62 /* Don't assume that UNICODE is not defined.  */
63 # undef SetEnvironmentVariable
64 # define SetEnvironmentVariable SetEnvironmentVariableA
65 #endif
66 
67 static int
68 _unsetenv (const char *name)
69 {
70   size_t len;
71 #if !HAVE_DECL__PUTENV
72   char **ep;
73 #endif
74 
75   if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
76     {
77       __set_errno (EINVAL);
78       return -1;
79     }
80 
81   len = strlen (name);
82 
83 #if HAVE_DECL__PUTENV
84   {
85     int putenv_result, putenv_errno;
86     char *name_ = malloc (len + 2);
87     memcpy (name_, name, len);
88     name_[len] = '=';
89     name_[len + 1] = 0;
90     putenv_result = _putenv (name_);
91     putenv_errno = errno;
92     free (name_);
93     __set_errno (putenv_errno);
94     return putenv_result;
95   }
96 #else
97 
98   LOCK;
99 
100   ep = environ;
101   while (*ep != NULL)
102     if (!strncmp (*ep, name, len) && (*ep)[len] == '=')
103       {
104         /* Found it.  Remove this pointer by moving later ones back.  */
105         char **dp = ep;
106 
107         do
108           dp[0] = dp[1];
109         while (*dp++);
110         /* Continue the loop in case NAME appears again.  */
111       }
112     else
113       ++ep;
114 
115   UNLOCK;
116 
117   return 0;
118 #endif
119 }
120 
121 
122 /* Put STRING, which is of the form "NAME=VALUE", in the environment.
123    If STRING contains no '=', then remove STRING from the environment.  */
124 int
putenv(char * string)125 putenv (char *string)
126 {
127   const char *name_end = strchr (string, '=');
128   char **ep;
129 
130   if (name_end == NULL)
131     {
132       /* Remove the variable from the environment.  */
133       return _unsetenv (string);
134     }
135 
136 #if HAVE_DECL__PUTENV
137   /* Rely on _putenv to allocate the new environment.  If other
138      parts of the application use _putenv, the !HAVE_DECL__PUTENV code
139      would fight over who owns the environ vector, causing a crash.  */
140   if (name_end[1])
141     return _putenv (string);
142   else
143     {
144       /* _putenv ("NAME=") unsets NAME, so invoke _putenv ("NAME= ")
145          to allocate the environ vector and then replace the new
146          entry with "NAME=".  */
147       int putenv_result, putenv_errno;
148       char *name_x = malloc (name_end - string + sizeof "= ");
149       if (!name_x)
150         return -1;
151       memcpy (name_x, string, name_end - string + 1);
152       name_x[name_end - string + 1] = ' ';
153       name_x[name_end - string + 2] = 0;
154       putenv_result = _putenv (name_x);
155       putenv_errno = errno;
156       for (ep = environ; *ep; ep++)
157         if (strcmp (*ep, name_x) == 0)
158           {
159             *ep = string;
160             break;
161           }
162 # if defined _WIN32 && ! defined __CYGWIN__
163       if (putenv_result == 0)
164         {
165           /* _putenv propagated "NAME= " into the subprocess environment;
166              fix that by calling SetEnvironmentVariable directly.  */
167           name_x[name_end - string] = 0;
168           putenv_result = SetEnvironmentVariable (name_x, "") ? 0 : -1;
169           putenv_errno = ENOMEM; /* ENOMEM is the only way to fail.  */
170         }
171 # endif
172       free (name_x);
173       __set_errno (putenv_errno);
174       return putenv_result;
175     }
176 #else
177   for (ep = environ; *ep; ep++)
178     if (strncmp (*ep, string, name_end - string) == 0
179         && (*ep)[name_end - string] == '=')
180       break;
181 
182   if (*ep)
183     *ep = string;
184   else
185     {
186       static char **last_environ = NULL;
187       size_t size = ep - environ;
188       char **new_environ = malloc ((size + 2) * sizeof *new_environ);
189       if (! new_environ)
190         return -1;
191       new_environ[0] = string;
192       memcpy (new_environ + 1, environ, (size + 1) * sizeof *new_environ);
193       free (last_environ);
194       last_environ = new_environ;
195       environ = new_environ;
196     }
197 
198   return 0;
199 #endif
200 }
201