check_ntp_time: update copyright
[monitoring-plugins.git] / gl / setenv.c
blobf0b889969f55695a6d52bc4a0df5d4625921f4f7
1 /* Copyright (C) 1992, 1995-2003, 2005-2023 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 ({ \
86 void *value = tfind (Str, &known_values, (compar_fn_t) strcmp); \
87 value != NULL ? *(char **) value : NULL; \
89 # define STORE_VALUE(Str) \
90 tsearch (Str, &known_values, (compar_fn_t) strcmp)
92 #else
93 # undef USE_TSEARCH
95 # define KNOWN_VALUE(Str) NULL
96 # define STORE_VALUE(Str) do { } while (0)
98 #endif
101 /* If this variable is not a null pointer we allocated the current
102 environment. */
103 static char **last_environ;
106 /* This function is used by 'setenv' and 'putenv'. The difference between
107 the two functions is that for the former must create a new string which
108 is then placed in the environment, while the argument of 'putenv'
109 must be used directly. This is all complicated by the fact that we try
110 to reuse values once generated for a 'setenv' call since we can never
111 free the strings. */
113 __add_to_environ (const char *name, const char *value, const char *combined,
114 int replace)
116 char **ep;
117 size_t size;
118 const size_t namelen = strlen (name);
119 const size_t vallen = value != NULL ? strlen (value) + 1 : 0;
121 LOCK;
123 /* We have to get the pointer now that we have the lock and not earlier
124 since another thread might have created a new environment. */
125 ep = __environ;
127 size = 0;
128 if (ep != NULL)
130 for (; *ep != NULL; ++ep)
131 if (!strncmp (*ep, name, namelen) && (*ep)[namelen] == '=')
132 break;
133 else
134 ++size;
137 if (ep == NULL || *ep == NULL)
139 char **new_environ;
140 #ifdef USE_TSEARCH
141 char *new_value;
142 #endif
144 /* We allocated this space; we can extend it. */
145 new_environ =
146 (char **) (last_environ == NULL
147 ? malloc ((size + 2) * sizeof (char *))
148 : realloc (last_environ, (size + 2) * sizeof (char *)));
149 if (new_environ == NULL)
151 /* It's easier to set errno to ENOMEM than to rely on the
152 'malloc-posix' and 'realloc-posix' gnulib modules. */
153 __set_errno (ENOMEM);
154 UNLOCK;
155 return -1;
158 /* If the whole entry is given add it. */
159 if (combined != NULL)
160 /* We must not add the string to the search tree since it belongs
161 to the user. */
162 new_environ[size] = (char *) combined;
163 else
165 /* See whether the value is already known. */
166 #ifdef USE_TSEARCH
167 # ifdef _LIBC
168 new_value = (char *) alloca (namelen + 1 + vallen);
169 __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1),
170 value, vallen);
171 # else
172 new_value = (char *) malloca (namelen + 1 + vallen);
173 if (new_value == NULL)
175 __set_errno (ENOMEM);
176 UNLOCK;
177 return -1;
179 memcpy (new_value, name, namelen);
180 new_value[namelen] = '=';
181 memcpy (&new_value[namelen + 1], value, vallen);
182 # endif
184 new_environ[size] = KNOWN_VALUE (new_value);
185 if (new_environ[size] == NULL)
186 #endif
188 new_environ[size] = (char *) malloc (namelen + 1 + vallen);
189 if (new_environ[size] == NULL)
191 #if defined USE_TSEARCH && !defined _LIBC
192 freea (new_value);
193 #endif
194 __set_errno (ENOMEM);
195 UNLOCK;
196 return -1;
199 #ifdef USE_TSEARCH
200 memcpy (new_environ[size], new_value, namelen + 1 + vallen);
201 #else
202 memcpy (new_environ[size], name, namelen);
203 new_environ[size][namelen] = '=';
204 memcpy (&new_environ[size][namelen + 1], value, vallen);
205 #endif
206 /* And save the value now. We cannot do this when we remove
207 the string since then we cannot decide whether it is a
208 user string or not. */
209 STORE_VALUE (new_environ[size]);
211 #if defined USE_TSEARCH && !defined _LIBC
212 freea (new_value);
213 #endif
216 if (__environ != last_environ)
217 memcpy ((char *) new_environ, (char *) __environ,
218 size * sizeof (char *));
220 new_environ[size + 1] = NULL;
222 last_environ = __environ = new_environ;
224 else if (replace)
226 char *np;
228 /* Use the user string if given. */
229 if (combined != NULL)
230 np = (char *) combined;
231 else
233 #ifdef USE_TSEARCH
234 char *new_value;
235 # ifdef _LIBC
236 new_value = alloca (namelen + 1 + vallen);
237 __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1),
238 value, vallen);
239 # else
240 new_value = malloca (namelen + 1 + vallen);
241 if (new_value == NULL)
243 __set_errno (ENOMEM);
244 UNLOCK;
245 return -1;
247 memcpy (new_value, name, namelen);
248 new_value[namelen] = '=';
249 memcpy (&new_value[namelen + 1], value, vallen);
250 # endif
252 np = KNOWN_VALUE (new_value);
253 if (np == NULL)
254 #endif
256 np = (char *) malloc (namelen + 1 + vallen);
257 if (np == NULL)
259 #if defined USE_TSEARCH && !defined _LIBC
260 freea (new_value);
261 #endif
262 __set_errno (ENOMEM);
263 UNLOCK;
264 return -1;
267 #ifdef USE_TSEARCH
268 memcpy (np, new_value, namelen + 1 + vallen);
269 #else
270 memcpy (np, name, namelen);
271 np[namelen] = '=';
272 memcpy (&np[namelen + 1], value, vallen);
273 #endif
274 /* And remember the value. */
275 STORE_VALUE (np);
277 #if defined USE_TSEARCH && !defined _LIBC
278 freea (new_value);
279 #endif
282 *ep = np;
285 UNLOCK;
287 return 0;
291 setenv (const char *name, const char *value, int replace)
293 if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
295 __set_errno (EINVAL);
296 return -1;
299 return __add_to_environ (name, value, NULL, replace);
302 /* The 'clearenv' was planned to be added to POSIX.1 but probably
303 never made it. Nevertheless the POSIX.9 standard (POSIX bindings
304 for Fortran 77) requires this function. */
306 clearenv (void)
308 LOCK;
310 if (__environ == last_environ && __environ != NULL)
312 /* We allocated this environment so we can free it. */
313 free (__environ);
314 last_environ = NULL;
317 /* Clear the environment pointer removes the whole environment. */
318 __environ = NULL;
320 UNLOCK;
322 return 0;
325 #ifdef _LIBC
326 static void
327 free_mem (void)
329 /* Remove all traces. */
330 clearenv ();
332 /* Now remove the search tree. */
333 __tdestroy (known_values, free);
334 known_values = NULL;
336 text_set_element (__libc_subfreeres, free_mem);
339 # undef setenv
340 # undef clearenv
341 weak_alias (__setenv, setenv)
342 weak_alias (__clearenv, clearenv)
343 #endif
345 #endif /* _LIBC || !HAVE_SETENV */
347 /* The rest of this file is called into use when replacing an existing
348 but buggy setenv. Known bugs include failure to diagnose invalid
349 name, and consuming a leading '=' from value. */
350 #if HAVE_SETENV
352 # undef setenv
353 # if !HAVE_DECL_SETENV
354 extern int setenv (const char *, const char *, int);
355 # endif
356 # define STREQ(a, b) (strcmp (a, b) == 0)
359 rpl_setenv (const char *name, const char *value, int replace)
361 int result;
362 if (!name || !*name || strchr (name, '='))
364 errno = EINVAL;
365 return -1;
367 /* Call the real setenv even if replace is 0, in case implementation
368 has underlying data to update, such as when environ changes. */
369 result = setenv (name, value, replace);
370 if (result == 0 && replace && *value == '=')
372 char *tmp = getenv (name);
373 if (!STREQ (tmp, value))
375 int saved_errno;
376 size_t len = strlen (value);
377 tmp = malloca (len + 2);
378 /* Since leading '=' is eaten, double it up. */
379 *tmp = '=';
380 memcpy (tmp + 1, value, len + 1);
381 result = setenv (name, tmp, replace);
382 saved_errno = errno;
383 freea (tmp);
384 errno = saved_errno;
387 return result;
390 #endif /* HAVE_SETENV */