1 /* -*- buffer-read-only: t -*- vi: set ro: */
2 /* DO NOT EDIT! GENERATED AUTOMATICALLY! */
3 /* Copyright (C) 1992, 1995-2003, 2005-2019 Free Software Foundation, Inc.
4 This file is part of the GNU C Library.
5
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <https://www.gnu.org/licenses/>. */
18
19 #if !_LIBC
20 /* Don't use __attribute__ __nonnull__ in this compilation unit. Otherwise gcc
21 optimizes away the name == NULL test below. */
22 # define _GL_ARG_NONNULL(params)
23
24 # define _GL_USE_STDLIB_ALLOC 1
25 # include <config.h>
26 #endif
27
28 #include <alloca.h>
29
30 /* Specification. */
31 #include <stdlib.h>
32
33 #include <errno.h>
34 #ifndef __set_errno
35 # define __set_errno(ev) ((errno) = (ev))
36 #endif
37
38 #include <string.h>
39 #if _LIBC || HAVE_UNISTD_H
40 # include <unistd.h>
41 #endif
42
43 #if !_LIBC
44 # include "malloca.h"
45 #endif
46
47 #if _LIBC || !HAVE_SETENV
48
49 #if !_LIBC
50 # define __environ environ
51 #endif
52
53 #if _LIBC
54 /* This lock protects against simultaneous modifications of 'environ'. */
55 # include <bits/libc-lock.h>
56 __libc_lock_define_initialized (static, envlock)
57 # define LOCK __libc_lock_lock (envlock)
58 # define UNLOCK __libc_lock_unlock (envlock)
59 #else
60 # define LOCK
61 # define UNLOCK
62 #endif
63
64 /* In the GNU C library we must keep the namespace clean. */
65 #ifdef _LIBC
66 # define setenv __setenv
67 # define clearenv __clearenv
68 # define tfind __tfind
69 # define tsearch __tsearch
70 #endif
71
72 /* In the GNU C library implementation we try to be more clever and
73 allow arbitrarily many changes of the environment given that the used
74 values are from a small set. Outside glibc this will eat up all
75 memory after a while. */
76 #if defined _LIBC || (defined HAVE_SEARCH_H && defined HAVE_TSEARCH \
77 && defined __GNUC__)
78 # define USE_TSEARCH 1
79 # include <search.h>
80 typedef int (*compar_fn_t) (const void *, const void *);
81
82 /* This is a pointer to the root of the search tree with the known
83 values. */
84 static void *known_values;
85
86 # define KNOWN_VALUE(Str) \
87 ({ \
88 void *value = tfind (Str, &known_values, (compar_fn_t) strcmp); \
89 value != NULL ? *(char **) value : NULL; \
90 })
91 # define STORE_VALUE(Str) \
92 tsearch (Str, &known_values, (compar_fn_t) strcmp)
93
94 #else
95 # undef USE_TSEARCH
96
97 # define KNOWN_VALUE(Str) NULL
98 # define STORE_VALUE(Str) do { } while (0)
99
100 #endif
101
102
103 /* If this variable is not a null pointer we allocated the current
104 environment. */
105 static char **last_environ;
106
107
108 /* This function is used by 'setenv' and 'putenv'. The difference between
109 the two functions is that for the former must create a new string which
110 is then placed in the environment, while the argument of 'putenv'
111 must be used directly. This is all complicated by the fact that we try
112 to reuse values once generated for a 'setenv' call since we can never
113 free the strings. */
114 int
__add_to_environ(const char * name,const char * value,const char * combined,int replace)115 __add_to_environ (const char *name, const char *value, const char *combined,
116 int replace)
117 {
118 char **ep;
119 size_t size;
120 const size_t namelen = strlen (name);
121 const size_t vallen = value != NULL ? strlen (value) + 1 : 0;
122
123 LOCK;
124
125 /* We have to get the pointer now that we have the lock and not earlier
126 since another thread might have created a new environment. */
127 ep = __environ;
128
129 size = 0;
130 if (ep != NULL)
131 {
132 for (; *ep != NULL; ++ep)
133 if (!strncmp (*ep, name, namelen) && (*ep)[namelen] == '=')
134 break;
135 else
136 ++size;
137 }
138
139 if (ep == NULL || *ep == NULL)
140 {
141 char **new_environ;
142 #ifdef USE_TSEARCH
143 char *new_value;
144 #endif
145
146 /* We allocated this space; we can extend it. */
147 new_environ =
148 (char **) (last_environ == NULL
149 ? malloc ((size + 2) * sizeof (char *))
150 : realloc (last_environ, (size + 2) * sizeof (char *)));
151 if (new_environ == NULL)
152 {
153 /* It's easier to set errno to ENOMEM than to rely on the
154 'malloc-posix' and 'realloc-posix' gnulib modules. */
155 __set_errno (ENOMEM);
156 UNLOCK;
157 return -1;
158 }
159
160 /* If the whole entry is given add it. */
161 if (combined != NULL)
162 /* We must not add the string to the search tree since it belongs
163 to the user. */
164 new_environ[size] = (char *) combined;
165 else
166 {
167 /* See whether the value is already known. */
168 #ifdef USE_TSEARCH
169 # ifdef _LIBC
170 new_value = (char *) alloca (namelen + 1 + vallen);
171 __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1),
172 value, vallen);
173 # else
174 new_value = (char *) malloca (namelen + 1 + vallen);
175 if (new_value == NULL)
176 {
177 __set_errno (ENOMEM);
178 UNLOCK;
179 return -1;
180 }
181 memcpy (new_value, name, namelen);
182 new_value[namelen] = '=';
183 memcpy (&new_value[namelen + 1], value, vallen);
184 # endif
185
186 new_environ[size] = KNOWN_VALUE (new_value);
187 if (new_environ[size] == NULL)
188 #endif
189 {
190 new_environ[size] = (char *) malloc (namelen + 1 + vallen);
191 if (new_environ[size] == NULL)
192 {
193 #if defined USE_TSEARCH && !defined _LIBC
194 freea (new_value);
195 #endif
196 __set_errno (ENOMEM);
197 UNLOCK;
198 return -1;
199 }
200
201 #ifdef USE_TSEARCH
202 memcpy (new_environ[size], new_value, namelen + 1 + vallen);
203 #else
204 memcpy (new_environ[size], name, namelen);
205 new_environ[size][namelen] = '=';
206 memcpy (&new_environ[size][namelen + 1], value, vallen);
207 #endif
208 /* And save the value now. We cannot do this when we remove
209 the string since then we cannot decide whether it is a
210 user string or not. */
211 STORE_VALUE (new_environ[size]);
212 }
213 #if defined USE_TSEARCH && !defined _LIBC
214 freea (new_value);
215 #endif
216 }
217
218 if (__environ != last_environ)
219 memcpy ((char *) new_environ, (char *) __environ,
220 size * sizeof (char *));
221
222 new_environ[size + 1] = NULL;
223
224 last_environ = __environ = new_environ;
225 }
226 else if (replace)
227 {
228 char *np;
229
230 /* Use the user string if given. */
231 if (combined != NULL)
232 np = (char *) combined;
233 else
234 {
235 #ifdef USE_TSEARCH
236 char *new_value;
237 # ifdef _LIBC
238 new_value = alloca (namelen + 1 + vallen);
239 __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1),
240 value, vallen);
241 # else
242 new_value = malloca (namelen + 1 + vallen);
243 if (new_value == NULL)
244 {
245 __set_errno (ENOMEM);
246 UNLOCK;
247 return -1;
248 }
249 memcpy (new_value, name, namelen);
250 new_value[namelen] = '=';
251 memcpy (&new_value[namelen + 1], value, vallen);
252 # endif
253
254 np = KNOWN_VALUE (new_value);
255 if (np == NULL)
256 #endif
257 {
258 np = (char *) malloc (namelen + 1 + vallen);
259 if (np == NULL)
260 {
261 #if defined USE_TSEARCH && !defined _LIBC
262 freea (new_value);
263 #endif
264 __set_errno (ENOMEM);
265 UNLOCK;
266 return -1;
267 }
268
269 #ifdef USE_TSEARCH
270 memcpy (np, new_value, namelen + 1 + vallen);
271 #else
272 memcpy (np, name, namelen);
273 np[namelen] = '=';
274 memcpy (&np[namelen + 1], value, vallen);
275 #endif
276 /* And remember the value. */
277 STORE_VALUE (np);
278 }
279 #if defined USE_TSEARCH && !defined _LIBC
280 freea (new_value);
281 #endif
282 }
283
284 *ep = np;
285 }
286
287 UNLOCK;
288
289 return 0;
290 }
291
292 int
setenv(const char * name,const char * value,int replace)293 setenv (const char *name, const char *value, int replace)
294 {
295 if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
296 {
297 __set_errno (EINVAL);
298 return -1;
299 }
300
301 return __add_to_environ (name, value, NULL, replace);
302 }
303
304 /* The 'clearenv' was planned to be added to POSIX.1 but probably
305 never made it. Nevertheless the POSIX.9 standard (POSIX bindings
306 for Fortran 77) requires this function. */
307 int
clearenv(void)308 clearenv (void)
309 {
310 LOCK;
311
312 if (__environ == last_environ && __environ != NULL)
313 {
314 /* We allocated this environment so we can free it. */
315 free (__environ);
316 last_environ = NULL;
317 }
318
319 /* Clear the environment pointer removes the whole environment. */
320 __environ = NULL;
321
322 UNLOCK;
323
324 return 0;
325 }
326
327 #ifdef _LIBC
328 static void
free_mem(void)329 free_mem (void)
330 {
331 /* Remove all traces. */
332 clearenv ();
333
334 /* Now remove the search tree. */
335 __tdestroy (known_values, free);
336 known_values = NULL;
337 }
338 text_set_element (__libc_subfreeres, free_mem);
339
340
341 # undef setenv
342 # undef clearenv
343 weak_alias (__setenv, setenv)
344 weak_alias (__clearenv, clearenv)
345 #endif
346
347 #endif /* _LIBC || !HAVE_SETENV */
348
349 /* The rest of this file is called into use when replacing an existing
350 but buggy setenv. Known bugs include failure to diagnose invalid
351 name, and consuming a leading '=' from value. */
352 #if HAVE_SETENV
353
354 # undef setenv
355 # if !HAVE_DECL_SETENV
356 extern int setenv (const char *, const char *, int);
357 # endif
358 # define STREQ(a, b) (strcmp (a, b) == 0)
359
360 int
rpl_setenv(const char * name,const char * value,int replace)361 rpl_setenv (const char *name, const char *value, int replace)
362 {
363 int result;
364 if (!name || !*name || strchr (name, '='))
365 {
366 errno = EINVAL;
367 return -1;
368 }
369 /* Call the real setenv even if replace is 0, in case implementation
370 has underlying data to update, such as when environ changes. */
371 result = setenv (name, value, replace);
372 if (result == 0 && replace && *value == '=')
373 {
374 char *tmp = getenv (name);
375 if (!STREQ (tmp, value))
376 {
377 int saved_errno;
378 size_t len = strlen (value);
379 tmp = malloca (len + 2);
380 /* Since leading '=' is eaten, double it up. */
381 *tmp = '=';
382 memcpy (tmp + 1, value, len + 1);
383 result = setenv (name, tmp, replace);
384 saved_errno = errno;
385 freea (tmp);
386 errno = saved_errno;
387 }
388 }
389 return result;
390 }
391
392 #endif /* HAVE_SETENV */
393