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