git-version-gen: Change suffix.
[gnulib.git] / lib / setenv.c
blobef301d4128a8a7eb123fffff9df52aac063f37cc
1 /* Copyright (C) 1992, 1995-2003, 2005-2025 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 defined _WIN32 && ! defined __CYGWIN__
42 # define WIN32_LEAN_AND_MEAN
43 # include <windows.h>
44 #endif
46 #if !_LIBC
47 # include "malloca.h"
48 #endif
50 #if defined _WIN32 && ! defined __CYGWIN__
51 /* Don't assume that UNICODE is not defined. */
52 # undef SetEnvironmentVariable
53 # define SetEnvironmentVariable SetEnvironmentVariableA
54 #endif
56 #if _LIBC || !HAVE_SETENV
57 #if !HAVE_DECL__PUTENV
59 #if !_LIBC
60 # define __environ environ
61 #endif
63 #if _LIBC
64 /* This lock protects against simultaneous modifications of 'environ'. */
65 # include <bits/libc-lock.h>
66 __libc_lock_define_initialized (static, envlock)
67 # define LOCK __libc_lock_lock (envlock)
68 # define UNLOCK __libc_lock_unlock (envlock)
69 #else
70 # define LOCK
71 # define UNLOCK
72 #endif
74 /* In the GNU C library we must keep the namespace clean. */
75 #ifdef _LIBC
76 # define setenv __setenv
77 # define clearenv __clearenv
78 # define tfind __tfind
79 # define tsearch __tsearch
80 #endif
82 /* In the GNU C library implementation we try to be more clever and
83 allow arbitrarily many changes of the environment given that the used
84 values are from a small set. Outside glibc this will eat up all
85 memory after a while. */
86 #if defined _LIBC || (defined HAVE_SEARCH_H && defined HAVE_TSEARCH \
87 && (defined __GNUC__ || defined __clang__))
88 # define USE_TSEARCH 1
89 # include <search.h>
90 typedef int (*compar_fn_t) (const void *, const void *);
92 /* This is a pointer to the root of the search tree with the known
93 values. */
94 static void *known_values;
96 # define KNOWN_VALUE(Str) \
97 __extension__ \
98 ({ \
99 void *value = tfind (Str, &known_values, (compar_fn_t) strcmp); \
100 value != NULL ? *(char **) value : NULL; \
102 # define STORE_VALUE(Str) \
103 tsearch (Str, &known_values, (compar_fn_t) strcmp)
105 #else
106 # undef USE_TSEARCH
108 # define KNOWN_VALUE(Str) NULL
109 # define STORE_VALUE(Str) do { } while (0)
111 #endif
114 /* If this variable is not a null pointer we allocated the current
115 environment. */
116 static char **last_environ;
119 /* This function is used by 'setenv' and 'putenv'. The difference between
120 the two functions is that for the former must create a new string which
121 is then placed in the environment, while the argument of 'putenv'
122 must be used directly. This is all complicated by the fact that we try
123 to reuse values once generated for a 'setenv' call since we can never
124 free the strings. */
126 __add_to_environ (const char *name, const char *value, const char *combined,
127 int replace)
129 char **ep;
130 size_t size;
131 const size_t namelen = strlen (name);
132 const size_t vallen = value != NULL ? strlen (value) + 1 : 0;
134 LOCK;
136 /* We have to get the pointer now that we have the lock and not earlier
137 since another thread might have created a new environment. */
138 ep = __environ;
140 size = 0;
141 if (ep != NULL)
143 for (; *ep != NULL; ++ep)
144 if (!strncmp (*ep, name, namelen) && (*ep)[namelen] == '=')
145 break;
146 else
147 ++size;
150 if (ep == NULL || *ep == NULL)
152 char **new_environ;
153 #ifdef USE_TSEARCH
154 char *new_value;
155 #endif
157 /* We allocated this space; we can extend it. */
158 new_environ =
159 (char **) (last_environ == NULL
160 ? malloc ((size + 2) * sizeof (char *))
161 : realloc (last_environ, (size + 2) * sizeof (char *)));
162 if (new_environ == NULL)
164 /* It's easier to set errno to ENOMEM than to rely on the
165 'malloc-posix' and 'realloc-posix' gnulib modules. */
166 __set_errno (ENOMEM);
167 UNLOCK;
168 return -1;
171 /* If the whole entry is given add it. */
172 if (combined != NULL)
173 /* We must not add the string to the search tree since it belongs
174 to the user. */
175 new_environ[size] = (char *) combined;
176 else
178 /* See whether the value is already known. */
179 #ifdef USE_TSEARCH
180 # ifdef _LIBC
181 new_value = (char *) alloca (namelen + 1 + vallen);
182 __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1),
183 value, vallen);
184 # else
185 new_value = (char *) malloca (namelen + 1 + vallen);
186 if (new_value == NULL)
188 __set_errno (ENOMEM);
189 UNLOCK;
190 return -1;
192 memcpy (new_value, name, namelen);
193 new_value[namelen] = '=';
194 memcpy (&new_value[namelen + 1], value, vallen);
195 # endif
197 new_environ[size] = KNOWN_VALUE (new_value);
198 if (new_environ[size] == NULL)
199 #endif
201 new_environ[size] = (char *) malloc (namelen + 1 + vallen);
202 if (new_environ[size] == NULL)
204 #if defined USE_TSEARCH && !defined _LIBC
205 freea (new_value);
206 #endif
207 __set_errno (ENOMEM);
208 UNLOCK;
209 return -1;
212 #ifdef USE_TSEARCH
213 memcpy (new_environ[size], new_value, namelen + 1 + vallen);
214 #else
215 memcpy (new_environ[size], name, namelen);
216 new_environ[size][namelen] = '=';
217 memcpy (&new_environ[size][namelen + 1], value, vallen);
218 #endif
219 /* And save the value now. We cannot do this when we remove
220 the string since then we cannot decide whether it is a
221 user string or not. */
222 STORE_VALUE (new_environ[size]);
224 #if defined USE_TSEARCH && !defined _LIBC
225 freea (new_value);
226 #endif
229 if (__environ != last_environ)
230 memcpy (new_environ, __environ, size * sizeof (char *));
232 new_environ[size + 1] = NULL;
234 last_environ = __environ = new_environ;
236 else if (replace)
238 char *np;
240 /* Use the user string if given. */
241 if (combined != NULL)
242 np = (char *) combined;
243 else
245 #ifdef USE_TSEARCH
246 char *new_value;
247 # ifdef _LIBC
248 new_value = alloca (namelen + 1 + vallen);
249 __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1),
250 value, vallen);
251 # else
252 new_value = malloca (namelen + 1 + vallen);
253 if (new_value == NULL)
255 __set_errno (ENOMEM);
256 UNLOCK;
257 return -1;
259 memcpy (new_value, name, namelen);
260 new_value[namelen] = '=';
261 memcpy (&new_value[namelen + 1], value, vallen);
262 # endif
264 np = KNOWN_VALUE (new_value);
265 if (np == NULL)
266 #endif
268 np = (char *) malloc (namelen + 1 + vallen);
269 if (np == NULL)
271 #if defined USE_TSEARCH && !defined _LIBC
272 freea (new_value);
273 #endif
274 __set_errno (ENOMEM);
275 UNLOCK;
276 return -1;
279 #ifdef USE_TSEARCH
280 memcpy (np, new_value, namelen + 1 + vallen);
281 #else
282 memcpy (np, name, namelen);
283 np[namelen] = '=';
284 memcpy (&np[namelen + 1], value, vallen);
285 #endif
286 /* And remember the value. */
287 STORE_VALUE (np);
289 #if defined USE_TSEARCH && !defined _LIBC
290 freea (new_value);
291 #endif
294 *ep = np;
297 UNLOCK;
299 return 0;
303 setenv (const char *name, const char *value, int replace)
305 if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
307 __set_errno (EINVAL);
308 return -1;
311 return __add_to_environ (name, value, NULL, replace);
314 /* The 'clearenv' was planned to be added to POSIX.1 but probably
315 never made it. Nevertheless the POSIX.9 standard (POSIX bindings
316 for Fortran 77) requires this function. */
318 clearenv (void)
320 LOCK;
322 if (__environ == last_environ && __environ != NULL)
324 /* We allocated this environment so we can free it. */
325 free (__environ);
326 last_environ = NULL;
329 /* Clear the environment pointer removes the whole environment. */
330 __environ = NULL;
332 UNLOCK;
334 return 0;
337 #ifdef _LIBC
338 static void
339 free_mem (void)
341 /* Remove all traces. */
342 clearenv ();
344 /* Now remove the search tree. */
345 __tdestroy (known_values, free);
346 known_values = NULL;
348 text_set_element (__libc_subfreeres, free_mem);
351 # undef setenv
352 # undef clearenv
353 weak_alias (__setenv, setenv)
354 weak_alias (__clearenv, clearenv)
355 #endif
357 #else /* HAVE_DECL__PUTENV */
358 /* Native Windows */
361 setenv (const char *name, const char *value, int replace)
363 if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
365 errno = EINVAL;
366 return -1;
369 /* The Microsoft documentation
370 <https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/putenv-wputenv>
371 says:
372 "Don't change an environment entry directly: instead,
373 use _putenv or _wputenv to change it."
374 Note: Microsoft's _putenv updates not only the contents of _environ but
375 also the contents of _wenviron, so that both are in kept in sync. */
376 const char *existing_value = getenv (name);
377 if (existing_value != NULL)
379 if (replace)
381 if (strcmp (existing_value, value) == 0)
382 /* No need to allocate memory. */
383 return 0;
385 else
386 /* Keep the existing value. */
387 return 0;
389 /* Allocate a new environment entry in the heap. */
390 /* _putenv ("NAME=") unsets NAME, so if VALUE is the empty string, invoke
391 _putenv ("NAME= ") and fix up the result afterwards. */
392 const char *value_ = (value[0] == '\0' ? " " : value);
393 size_t name_len = strlen (name);
394 size_t value_len = strlen (value_);
395 char *string = (char *) malloc (name_len + 1 + value_len + 1);
396 if (string == NULL)
397 return -1;
398 memcpy (string, name, name_len);
399 string[name_len] = '=';
400 memcpy (&string[name_len + 1], value_, value_len + 1);
401 /* Use _putenv. */
402 if (_putenv (string) < 0)
403 return -1;
404 if (value[0] == '\0')
406 /* Fix up the result. */
407 char *new_value = getenv (name);
408 if (new_value != NULL && new_value[0] == ' ' && new_value[1] == '\0')
409 new_value[0] = '\0';
410 # if defined _WIN32 && ! defined __CYGWIN__
411 /* _putenv propagated "NAME= " into the subprocess environment;
412 fix that by calling SetEnvironmentVariable directly. */
413 /* Documentation:
414 <https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setenvironmentvariable> */
415 if (!SetEnvironmentVariable (name, ""))
417 switch (GetLastError ())
419 case ERROR_NOT_ENOUGH_MEMORY:
420 case ERROR_OUTOFMEMORY:
421 errno = ENOMEM;
422 break;
423 default:
424 errno = EINVAL;
425 break;
427 return -1;
429 # endif
431 return 0;
434 #endif /* HAVE_DECL__PUTENV */
435 #endif /* _LIBC || !HAVE_SETENV */
437 /* The rest of this file is called into use when replacing an existing
438 but buggy setenv. Known bugs include failure to diagnose invalid
439 name, and consuming a leading '=' from value. */
440 #if HAVE_SETENV
442 # undef setenv
443 # if !HAVE_DECL_SETENV
444 extern int setenv (const char *, const char *, int);
445 # endif
446 # define STREQ(a, b) (strcmp (a, b) == 0)
449 rpl_setenv (const char *name, const char *value, int replace)
451 int result;
452 if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
454 errno = EINVAL;
455 return -1;
457 /* Call the real setenv even if replace is 0, in case implementation
458 has underlying data to update, such as when environ changes. */
459 result = setenv (name, value, replace);
460 if (result == 0 && replace && *value == '=')
462 char *tmp = getenv (name);
463 if (!STREQ (tmp, value))
465 int saved_errno;
466 size_t len = strlen (value);
467 tmp = malloca (len + 2);
468 if (tmp == NULL)
470 errno = ENOMEM;
471 return -1;
473 /* Since leading '=' is eaten, double it up. */
474 *tmp = '=';
475 memcpy (tmp + 1, value, len + 1);
476 result = setenv (name, tmp, replace);
477 saved_errno = errno;
478 freea (tmp);
479 errno = saved_errno;
482 return result;
485 #endif /* HAVE_SETENV */