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