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