Merged branch dev-0.4
[poldi.git] / src / assuan / setenv.c
1 /* Copyright (C) 1992,1995-2001,2004 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8
9    The GNU C Library 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 GNU
12    Lesser General Public License for more details.
13
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, 
16    see <http://www.gnu.org/licenses/>.
17  */
18
19 #if HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22
23 #include "assuan-defs.h"
24
25 #define __builtin_expect(cond,val) (cond)
26
27 #include <errno.h>
28 #if !_LIBC
29 # if !defined errno && !defined HAVE_ERRNO_DECL
30 extern int errno;
31 # endif
32 # define __set_errno(ev) ((errno) = (ev))
33 #endif
34
35 #if _LIBC || HAVE_STDLIB_H
36 # include <stdlib.h>
37 #endif
38 #if _LIBC || HAVE_STRING_H
39 # include <string.h>
40 #endif
41 #if _LIBC || HAVE_UNISTD_H
42 # include <unistd.h>
43 #endif
44
45 #if !_LIBC
46 # define __environ      environ
47 # ifndef HAVE_ENVIRON_DECL
48 extern char **environ;
49 # endif
50 #endif
51
52 #if _LIBC
53 /* This lock protects against simultaneous modifications of `environ'.  */
54 # include <bits/libc-lock.h>
55 __libc_lock_define_initialized (static, envlock)
56 # define LOCK   __libc_lock_lock (envlock)
57 # define UNLOCK __libc_lock_unlock (envlock)
58 #else
59 # define LOCK
60 # define UNLOCK
61 #endif
62
63 /* In the GNU C library we must keep the namespace clean.  */
64 #ifdef _LIBC
65 # define setenv __setenv
66 # define unsetenv __unsetenv
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
81 /* This is a pointer to the root of the search tree with the known
82    values.  */
83 static void *known_values;
84
85 # define KNOWN_VALUE(Str) \
86   ({                                                                          \
87     void *value = tfind (Str, &known_values, (__compar_fn_t) strcmp);         \
88     value != NULL ? *(char **) value : NULL;                                  \
89   })
90 # define STORE_VALUE(Str) \
91   tsearch (Str, &known_values, (__compar_fn_t) strcmp)
92
93 #else
94 # undef USE_TSEARCH
95
96 # define KNOWN_VALUE(Str) NULL
97 # define STORE_VALUE(Str) do { } while (0)
98
99 #endif
100
101
102 /* If this variable is not a null pointer we allocated the current
103    environment.  */
104 static char **last_environ;
105
106
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.  */
113 static int
114 __add_to_environ (const char *name, const char *value, const char *combined,
115                   int replace)
116 {
117   register char **ep;
118   register size_t size;
119   const size_t namelen = strlen (name);
120   const size_t vallen = value != NULL ? strlen (value) + 1 : 0;
121
122   LOCK;
123
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;
127
128   size = 0;
129   if (ep != NULL)
130     {
131       for (; *ep != NULL; ++ep)
132         if (!strncmp (*ep, name, namelen) && (*ep)[namelen] == '=')
133           break;
134         else
135           ++size;
136     }
137
138   if (ep == NULL || __builtin_expect (*ep == NULL, 1))
139     {
140       char **new_environ;
141
142       /* We allocated this space; we can extend it.  */
143       new_environ = (char **) realloc (last_environ,
144                                        (size + 2) * sizeof (char *));
145       if (new_environ == NULL)
146         {
147           UNLOCK;
148           return -1;
149         }
150
151       /* If the whole entry is given add it.  */
152       if (combined != NULL)
153         /* We must not add the string to the search tree since it belongs
154            to the user.  */
155         new_environ[size] = (char *) combined;
156       else
157         {
158           /* See whether the value is already known.  */
159 #ifdef USE_TSEARCH
160 # ifdef __GNUC__
161           char new_value[namelen + 1 + vallen];
162 # else
163           char *new_value = (char *) alloca (namelen + 1 + vallen);
164 # endif
165 # ifdef _LIBC
166           __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1),
167                      value, vallen);
168 # else
169           memcpy (new_value, name, namelen);
170           new_value[namelen] = '=';
171           memcpy (&new_value[namelen + 1], value, vallen);
172 # endif
173
174           new_environ[size] = KNOWN_VALUE (new_value);
175           if (__builtin_expect (new_environ[size] == NULL, 1))
176 #endif
177             {
178               new_environ[size] = (char *) malloc (namelen + 1 + vallen);
179               if (__builtin_expect (new_environ[size] == NULL, 0))
180                 {
181                   __set_errno (ENOMEM);
182                   UNLOCK;
183                   return -1;
184                 }
185
186 #ifdef USE_TSEARCH
187               memcpy (new_environ[size], new_value, namelen + 1 + vallen);
188 #else
189               memcpy (new_environ[size], name, namelen);
190               new_environ[size][namelen] = '=';
191               memcpy (&new_environ[size][namelen + 1], value, vallen);
192 #endif
193               /* And save the value now.  We cannot do this when we remove
194                  the string since then we cannot decide whether it is a
195                  user string or not.  */
196               STORE_VALUE (new_environ[size]);
197             }
198         }
199
200       if (__environ != last_environ)
201         memcpy ((char *) new_environ, (char *) __environ,
202                 size * sizeof (char *));
203
204       new_environ[size + 1] = NULL;
205
206       last_environ = __environ = new_environ;
207     }
208   else if (replace)
209     {
210       char *np;
211
212       /* Use the user string if given.  */
213       if (combined != NULL)
214         np = (char *) combined;
215       else
216         {
217 #ifdef USE_TSEARCH
218 # ifdef __GNUC__
219           char new_value[namelen + 1 + vallen];
220 # else
221           char *new_value = (char *) alloca (namelen + 1 + vallen);
222 # endif
223 # ifdef _LIBC
224           __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1),
225                      value, vallen);
226 # else
227           memcpy (new_value, name, namelen);
228           new_value[namelen] = '=';
229           memcpy (&new_value[namelen + 1], value, vallen);
230 # endif
231
232           np = KNOWN_VALUE (new_value);
233           if (__builtin_expect (np == NULL, 1))
234 #endif
235             {
236               np = malloc (namelen + 1 + vallen);
237               if (__builtin_expect (np == NULL, 0))
238                 {
239                   UNLOCK;
240                   return -1;
241                 }
242
243 #ifdef USE_TSEARCH
244               memcpy (np, new_value, namelen + 1 + vallen);
245 #else
246               memcpy (np, name, namelen);
247               np[namelen] = '=';
248               memcpy (&np[namelen + 1], value, vallen);
249 #endif
250               /* And remember the value.  */
251               STORE_VALUE (np);
252             }
253         }
254
255       *ep = np;
256     }
257
258   UNLOCK;
259
260   return 0;
261 }
262
263 int
264 setenv (const char *name, const char *value, int replace)
265 {
266   if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
267     {
268       __set_errno (EINVAL);
269       return -1;
270     }
271
272   return __add_to_environ (name, value, NULL, replace);
273 }
274
275 int
276 unsetenv (const char *name)
277 {
278   size_t len;
279   char **ep;
280
281   if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
282     {
283       __set_errno (EINVAL);
284       return -1;
285     }
286
287   len = strlen (name);
288
289   LOCK;
290
291   ep = __environ;
292   while (*ep != NULL)
293     if (!strncmp (*ep, name, len) && (*ep)[len] == '=')
294       {
295         /* Found it.  Remove this pointer by moving later ones back.  */
296         char **dp = ep;
297
298         do
299           dp[0] = dp[1];
300         while (*dp++);
301         /* Continue the loop in case NAME appears again.  */
302       }
303     else
304       ++ep;
305
306   UNLOCK;
307
308   return 0;
309 }
310
311 /* The `clearenv' was planned to be added to POSIX.1 but probably
312    never made it.  Nevertheless the POSIX.9 standard (POSIX bindings
313    for Fortran 77) requires this function.  */
314 int
315 clearenv (void)
316 {
317   LOCK;
318
319   if (__environ == last_environ && __environ != NULL)
320     {
321       /* We allocated this environment so we can free it.  */
322       free (__environ);
323       last_environ = NULL;
324     }
325
326   /* Clear the environment pointer removes the whole environment.  */
327   __environ = NULL;
328
329   UNLOCK;
330
331   return 0;
332 }
333 #ifdef _LIBC
334 libc_freeres_fn (free_mem)
335 {
336   /* Remove all traces.  */
337   clearenv ();
338
339   /* Now remove the search tree.  */
340   __tdestroy (known_values, free);
341   known_values = NULL;
342 }
343
344 # undef setenv
345 # undef unsetenv
346 # undef clearenv
347 weak_alias (__setenv, setenv)
348 weak_alias (__unsetenv, unsetenv)
349 weak_alias (__clearenv, clearenv)
350 #endif
351
352