2009-06-18 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / src / 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, write to the Free
16    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
17    02110-1301 USA.  */
18
19 #if HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22
23 #if HAVE_ASSUAN_H
24 /* Fixme: Why do we need to include the assuan header and why the
25    internal ones? */
26 #include "assuan-defs.h"
27 #endif /*HAVE_ASSUAN_H*/
28
29 #define __builtin_expect(cond,val) (cond)
30
31 #include <errno.h>
32 #if !_LIBC
33 # if !defined errno && !defined HAVE_ERRNO_DECL
34 extern int errno;
35 # endif
36 # define __set_errno(ev) ((errno) = (ev))
37 #endif
38
39 #if _LIBC || HAVE_STDLIB_H
40 # include <stdlib.h>
41 #endif
42 #if _LIBC || HAVE_STRING_H
43 # include <string.h>
44 #endif
45 #if _LIBC || HAVE_UNISTD_H
46 # include <unistd.h>
47 #endif
48
49 #if !_LIBC
50 # define __environ      environ
51 # ifndef HAVE_ENVIRON_DECL
52 extern char **environ;
53 # endif
54 #endif
55
56 #if _LIBC
57 /* This lock protects against simultaneous modifications of `environ'.  */
58 # include <bits/libc-lock.h>
59 __libc_lock_define_initialized (static, envlock)
60 # define LOCK   __libc_lock_lock (envlock)
61 # define UNLOCK __libc_lock_unlock (envlock)
62 #else
63 # define LOCK
64 # define UNLOCK
65 #endif
66
67 /* In the GNU C library we must keep the namespace clean.  */
68 #ifdef _LIBC
69 # define setenv __setenv
70 # define unsetenv __unsetenv
71 # define clearenv __clearenv
72 # define tfind __tfind
73 # define tsearch __tsearch
74 #endif
75
76 /* In the GNU C library implementation we try to be more clever and
77    allow arbitrarily many changes of the environment given that the used
78    values are from a small set.  Outside glibc this will eat up all
79    memory after a while.  */
80 #if defined _LIBC || (defined HAVE_SEARCH_H && defined HAVE_TSEARCH \
81                       && defined __GNUC__)
82 # define USE_TSEARCH    1
83 # include <search.h>
84
85 /* This is a pointer to the root of the search tree with the known
86    values.  */
87 static void *known_values;
88
89 # define KNOWN_VALUE(Str) \
90   ({                                                                          \
91     void *value = tfind (Str, &known_values, (__compar_fn_t) strcmp);         \
92     value != NULL ? *(char **) value : NULL;                                  \
93   })
94 # define STORE_VALUE(Str) \
95   tsearch (Str, &known_values, (__compar_fn_t) strcmp)
96
97 #else
98 # undef USE_TSEARCH
99
100 # define KNOWN_VALUE(Str) NULL
101 # define STORE_VALUE(Str) do { } while (0)
102
103 #endif
104
105
106 /* If this variable is not a null pointer we allocated the current
107    environment.  */
108 static char **last_environ;
109
110
111 /* This function is used by `setenv' and `putenv'.  The difference between
112    the two functions is that for the former must create a new string which
113    is then placed in the environment, while the argument of `putenv'
114    must be used directly.  This is all complicated by the fact that we try
115    to reuse values once generated for a `setenv' call since we can never
116    free the strings.  */
117 static int
118 __add_to_environ (const char *name, const char *value, const char *combined,
119                   int replace)
120 {
121   register char **ep;
122   register size_t size;
123   const size_t namelen = strlen (name);
124   const size_t vallen = value != NULL ? strlen (value) + 1 : 0;
125
126   LOCK;
127
128   /* We have to get the pointer now that we have the lock and not earlier
129      since another thread might have created a new environment.  */
130   ep = __environ;
131
132   size = 0;
133   if (ep != NULL)
134     {
135       for (; *ep != NULL; ++ep)
136         if (!strncmp (*ep, name, namelen) && (*ep)[namelen] == '=')
137           break;
138         else
139           ++size;
140     }
141
142   if (ep == NULL || __builtin_expect (*ep == NULL, 1))
143     {
144       char **new_environ;
145
146       /* We allocated this space; we can extend it.  */
147       new_environ = (char **) realloc (last_environ,
148                                        (size + 2) * sizeof (char *));
149       if (new_environ == NULL)
150         {
151           UNLOCK;
152           return -1;
153         }
154
155       /* If the whole entry is given add it.  */
156       if (combined != NULL)
157         /* We must not add the string to the search tree since it belongs
158            to the user.  */
159         new_environ[size] = (char *) combined;
160       else
161         {
162           /* See whether the value is already known.  */
163 #ifdef USE_TSEARCH
164 # ifdef __GNUC__
165           char new_value[namelen + 1 + vallen];
166 # else
167           char *new_value = (char *) alloca (namelen + 1 + vallen);
168 # endif
169 # ifdef _LIBC
170           __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1),
171                      value, vallen);
172 # else
173           memcpy (new_value, name, namelen);
174           new_value[namelen] = '=';
175           memcpy (&new_value[namelen + 1], value, vallen);
176 # endif
177
178           new_environ[size] = KNOWN_VALUE (new_value);
179           if (__builtin_expect (new_environ[size] == NULL, 1))
180 #endif
181             {
182               new_environ[size] = (char *) malloc (namelen + 1 + vallen);
183               if (__builtin_expect (new_environ[size] == NULL, 0))
184                 {
185                   __set_errno (ENOMEM);
186                   UNLOCK;
187                   return -1;
188                 }
189
190 #ifdef USE_TSEARCH
191               memcpy (new_environ[size], new_value, namelen + 1 + vallen);
192 #else
193               memcpy (new_environ[size], name, namelen);
194               new_environ[size][namelen] = '=';
195               memcpy (&new_environ[size][namelen + 1], value, vallen);
196 #endif
197               /* And save the value now.  We cannot do this when we remove
198                  the string since then we cannot decide whether it is a
199                  user string or not.  */
200               STORE_VALUE (new_environ[size]);
201             }
202         }
203
204       if (__environ != last_environ)
205         memcpy ((char *) new_environ, (char *) __environ,
206                 size * sizeof (char *));
207
208       new_environ[size + 1] = NULL;
209
210       last_environ = __environ = new_environ;
211     }
212   else if (replace)
213     {
214       char *np;
215
216       /* Use the user string if given.  */
217       if (combined != NULL)
218         np = (char *) combined;
219       else
220         {
221 #ifdef USE_TSEARCH
222 # ifdef __GNUC__
223           char new_value[namelen + 1 + vallen];
224 # else
225           char *new_value = (char *) alloca (namelen + 1 + vallen);
226 # endif
227 # ifdef _LIBC
228           __mempcpy (__mempcpy (__mempcpy (new_value, name, namelen), "=", 1),
229                      value, vallen);
230 # else
231           memcpy (new_value, name, namelen);
232           new_value[namelen] = '=';
233           memcpy (&new_value[namelen + 1], value, vallen);
234 # endif
235
236           np = KNOWN_VALUE (new_value);
237           if (__builtin_expect (np == NULL, 1))
238 #endif
239             {
240               np = malloc (namelen + 1 + vallen);
241               if (__builtin_expect (np == NULL, 0))
242                 {
243                   UNLOCK;
244                   return -1;
245                 }
246
247 #ifdef USE_TSEARCH
248               memcpy (np, new_value, namelen + 1 + vallen);
249 #else
250               memcpy (np, name, namelen);
251               np[namelen] = '=';
252               memcpy (&np[namelen + 1], value, vallen);
253 #endif
254               /* And remember the value.  */
255               STORE_VALUE (np);
256             }
257         }
258
259       *ep = np;
260     }
261
262   UNLOCK;
263
264   return 0;
265 }
266
267 int
268 setenv (const char *name, const char *value, int replace)
269 {
270   if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
271     {
272       __set_errno (EINVAL);
273       return -1;
274     }
275
276   return __add_to_environ (name, value, NULL, replace);
277 }
278
279 int
280 unsetenv (const char *name)
281 {
282   size_t len;
283   char **ep;
284
285   if (name == NULL || *name == '\0' || strchr (name, '=') != NULL)
286     {
287       __set_errno (EINVAL);
288       return -1;
289     }
290
291   len = strlen (name);
292
293   LOCK;
294
295   ep = __environ;
296   while (*ep != NULL)
297     if (!strncmp (*ep, name, len) && (*ep)[len] == '=')
298       {
299         /* Found it.  Remove this pointer by moving later ones back.  */
300         char **dp = ep;
301
302         do
303           dp[0] = dp[1];
304         while (*dp++);
305         /* Continue the loop in case NAME appears again.  */
306       }
307     else
308       ++ep;
309
310   UNLOCK;
311
312   return 0;
313 }
314
315 /* The `clearenv' was planned to be added to POSIX.1 but probably
316    never made it.  Nevertheless the POSIX.9 standard (POSIX bindings
317    for Fortran 77) requires this function.  */
318 int
319 clearenv (void)
320 {
321   LOCK;
322
323   if (__environ == last_environ && __environ != NULL)
324     {
325       /* We allocated this environment so we can free it.  */
326       free (__environ);
327       last_environ = NULL;
328     }
329
330   /* Clear the environment pointer removes the whole environment.  */
331   __environ = NULL;
332
333   UNLOCK;
334
335   return 0;
336 }
337 #ifdef _LIBC
338 libc_freeres_fn (free_mem)
339 {
340   /* Remove all traces.  */
341   clearenv ();
342
343   /* Now remove the search tree.  */
344   __tdestroy (known_values, free);
345   known_values = NULL;
346 }
347
348 # undef setenv
349 # undef unsetenv
350 # undef clearenv
351 weak_alias (__setenv, setenv)
352 weak_alias (__unsetenv, unsetenv)
353 weak_alias (__clearenv, clearenv)
354 #endif
355
356