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