Sync libc/stdlib with FreeBSD (ignoring jemalloc, pts, and gdtoa):
[dragonfly.git] / lib / libc / stdlib / getenv.c
blob731b3c50e4a20a384d1ebe6f7588c88e790440f8
1 /*-
2 * Copyright (c) 2007-2008 Sean C. Farley <scf@FreeBSD.org>
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer,
10 * without modification, immediately at the beginning of the file.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 * $FreeBSD: src/lib/libc/stdlib/getenv.c,v 1.15 2008/08/03 22:47:23 scf Exp $
27 * $DragonFly: src/lib/libc/stdlib/getenv.c,v 1.5 2005/04/28 13:51:55 joerg Exp $
30 #include "namespace.h"
31 #include <sys/types.h>
32 #include <errno.h>
33 #include <stdbool.h>
34 #include <stddef.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include "un-namespace.h"
41 static const char CorruptEnvFindMsg[] = "environment corrupt; unable to find ";
42 static const char CorruptEnvValueMsg[] =
43 "environment corrupt; missing value for ";
47 * Standard environ. environ variable is exposed to entire process.
49 * origEnviron: Upon cleanup on unloading of library or failure, this
50 * allows environ to return to as it was before.
51 * environSize: Number of variables environ can hold. Can only
52 * increase.
53 * intEnviron: Internally-built environ. Exposed via environ during
54 * (re)builds of the environment.
56 extern char **environ;
57 static char **origEnviron;
58 static char **intEnviron = NULL;
59 static int environSize = 0;
62 * Array of environment variables built from environ. Each element records:
63 * name: Pointer to name=value string
64 * name length: Length of name not counting '=' character
65 * value: Pointer to value within same string as name
66 * value size: Size (not length) of space for value not counting the
67 * nul character
68 * active state: true/false value to signify whether variable is active.
69 * Useful since multiple variables with the same name can
70 * co-exist. At most, one variable can be active at any
71 * one time.
72 * putenv: Created from putenv() call. This memory must not be
73 * reused.
75 static struct envVars {
76 size_t nameLen;
77 size_t valueSize;
78 char *name;
79 char *value;
80 bool active;
81 bool putenv;
82 } *envVars = NULL;
85 * Environment array information.
87 * envActive: Number of active variables in array.
88 * envVarsSize: Size of array.
89 * envVarsTotal: Number of total variables in array (active or not).
91 static int envActive = 0;
92 static int envVarsSize = 0;
93 static int envVarsTotal = 0;
96 /* Deinitialization of new environment. */
97 static void __attribute__ ((destructor)) __clean_env_destructor(void);
101 * A simple version of warnx() to avoid the bloat of including stdio in static
102 * binaries.
104 static void
105 __env_warnx(const char *msg, const char *name, size_t nameLen)
107 static const char nl[] = "\n";
108 static const char progSep[] = ": ";
110 _write(STDERR_FILENO, _getprogname(), strlen(_getprogname()));
111 _write(STDERR_FILENO, progSep, sizeof(progSep) - 1);
112 _write(STDERR_FILENO, msg, strlen(msg));
113 _write(STDERR_FILENO, name, nameLen);
114 _write(STDERR_FILENO, nl, sizeof(nl) - 1);
116 return;
121 * Inline strlen() for performance. Also, perform check for an equals sign.
122 * Cheaper here than peforming a strchr() later.
124 static inline size_t
125 __strleneq(const char *str)
127 const char *s;
129 for (s = str; *s != '\0'; ++s)
130 if (*s == '=')
131 return (0);
133 return (s - str);
138 * Comparison of an environment name=value to a name.
140 static inline bool
141 strncmpeq(const char *nameValue, const char *name, size_t nameLen)
143 if (strncmp(nameValue, name, nameLen) == 0 && nameValue[nameLen] == '=')
144 return (true);
146 return (false);
151 * Using environment, returns pointer to value associated with name, if any,
152 * else NULL. If the onlyActive flag is set to true, only variables that are
153 * active are returned else all are.
155 static inline char *
156 __findenv(const char *name, size_t nameLen, int *envNdx, bool onlyActive)
158 int ndx;
161 * Find environment variable from end of array (more likely to be
162 * active). A variable created by putenv is always active or it is not
163 * tracked in the array.
165 for (ndx = *envNdx; ndx >= 0; ndx--)
166 if (envVars[ndx].putenv) {
167 if (strncmpeq(envVars[ndx].name, name, nameLen)) {
168 *envNdx = ndx;
169 return (envVars[ndx].name + nameLen +
170 sizeof ("=") - 1);
172 } else if ((!onlyActive || envVars[ndx].active) &&
173 (envVars[ndx].nameLen == nameLen &&
174 strncmpeq(envVars[ndx].name, name, nameLen))) {
175 *envNdx = ndx;
176 return (envVars[ndx].value);
179 return (NULL);
184 * Using environ, returns pointer to value associated with name, if any, else
185 * NULL. Used on the original environ passed into the program.
187 static char *
188 __findenv_environ(const char *name, size_t nameLen)
190 int envNdx;
192 /* Find variable within environ. */
193 for (envNdx = 0; environ[envNdx] != NULL; envNdx++)
194 if (strncmpeq(environ[envNdx], name, nameLen))
195 return (&(environ[envNdx][nameLen + sizeof("=") - 1]));
197 return (NULL);
202 * Remove variable added by putenv() from variable tracking array.
204 static void
205 __remove_putenv(int envNdx)
207 envVarsTotal--;
208 if (envVarsTotal > envNdx)
209 memmove(&(envVars[envNdx]), &(envVars[envNdx + 1]),
210 (envVarsTotal - envNdx) * sizeof (*envVars));
211 memset(&(envVars[envVarsTotal]), 0, sizeof (*envVars));
213 return;
218 * Deallocate the environment built from environ as well as environ then set
219 * both to NULL. Eases debugging of memory leaks.
221 static void
222 __clean_env(bool freeVars)
224 int envNdx;
226 /* Deallocate environment and environ if created by *env(). */
227 if (envVars != NULL) {
228 for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--)
229 /* Free variables or deactivate them. */
230 if (envVars[envNdx].putenv) {
231 if (!freeVars)
232 __remove_putenv(envNdx);
233 } else {
234 if (freeVars)
235 free(envVars[envNdx].name);
236 else
237 envVars[envNdx].active = false;
239 if (freeVars) {
240 free(envVars);
241 envVars = NULL;
242 } else
243 envActive = 0;
245 /* Restore original environ if it has not updated by program. */
246 if (origEnviron != NULL) {
247 if (environ == intEnviron)
248 environ = origEnviron;
249 free(intEnviron);
250 intEnviron = NULL;
251 environSize = 0;
255 return;
260 * Using the environment, rebuild the environ array for use by other C library
261 * calls that depend upon it.
263 static int
264 __rebuild_environ(int newEnvironSize)
266 char **tmpEnviron;
267 int envNdx;
268 int environNdx;
269 int tmpEnvironSize;
271 /* Resize environ. */
272 if (newEnvironSize > environSize) {
273 tmpEnvironSize = newEnvironSize * 2;
274 tmpEnviron = realloc(intEnviron, sizeof (*intEnviron) *
275 (tmpEnvironSize + 1));
276 if (tmpEnviron == NULL)
277 return (-1);
278 environSize = tmpEnvironSize;
279 intEnviron = tmpEnviron;
281 envActive = newEnvironSize;
283 /* Assign active variables to environ. */
284 for (envNdx = envVarsTotal - 1, environNdx = 0; envNdx >= 0; envNdx--)
285 if (envVars[envNdx].active)
286 intEnviron[environNdx++] = envVars[envNdx].name;
287 intEnviron[environNdx] = NULL;
289 /* Always set environ which may have been replaced by program. */
290 environ = intEnviron;
292 return (0);
297 * Enlarge new environment.
299 static inline bool
300 __enlarge_env(void)
302 int newEnvVarsSize;
303 struct envVars *tmpEnvVars;
305 envVarsTotal++;
306 if (envVarsTotal > envVarsSize) {
307 newEnvVarsSize = envVarsTotal * 2;
308 tmpEnvVars = realloc(envVars, sizeof (*envVars) *
309 newEnvVarsSize);
310 if (tmpEnvVars == NULL) {
311 envVarsTotal--;
312 return (false);
314 envVarsSize = newEnvVarsSize;
315 envVars = tmpEnvVars;
318 return (true);
323 * Using environ, build an environment for use by standard C library calls.
325 static int
326 __build_env(void)
328 char **env;
329 int activeNdx;
330 int envNdx;
331 int savedErrno;
332 size_t nameLen;
334 /* Check for non-existant environment. */
335 if (environ == NULL || environ[0] == NULL)
336 return (0);
338 /* Count environment variables. */
339 for (env = environ, envVarsTotal = 0; *env != NULL; env++)
340 envVarsTotal++;
341 envVarsSize = envVarsTotal * 2;
343 /* Create new environment. */
344 envVars = calloc(1, sizeof (*envVars) * envVarsSize);
345 if (envVars == NULL)
346 goto Failure;
348 /* Copy environ values and keep track of them. */
349 for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--) {
350 envVars[envNdx].putenv = false;
351 envVars[envNdx].name =
352 strdup(environ[envVarsTotal - envNdx - 1]);
353 if (envVars[envNdx].name == NULL)
354 goto Failure;
355 envVars[envNdx].value = strchr(envVars[envNdx].name, '=');
356 if (envVars[envNdx].value != NULL) {
357 envVars[envNdx].value++;
358 envVars[envNdx].valueSize =
359 strlen(envVars[envNdx].value);
360 } else {
361 __env_warnx(CorruptEnvValueMsg, envVars[envNdx].name,
362 strlen(envVars[envNdx].name));
363 errno = EFAULT;
364 goto Failure;
368 * Find most current version of variable to make active. This
369 * will prevent multiple active variables from being created
370 * during this initialization phase.
372 nameLen = envVars[envNdx].value - envVars[envNdx].name - 1;
373 envVars[envNdx].nameLen = nameLen;
374 activeNdx = envVarsTotal - 1;
375 if (__findenv(envVars[envNdx].name, nameLen, &activeNdx,
376 false) == NULL) {
377 __env_warnx(CorruptEnvFindMsg, envVars[envNdx].name,
378 nameLen);
379 errno = EFAULT;
380 goto Failure;
382 envVars[activeNdx].active = true;
385 /* Create a new environ. */
386 origEnviron = environ;
387 environ = NULL;
388 if (__rebuild_environ(envVarsTotal) == 0)
389 return (0);
391 Failure:
392 savedErrno = errno;
393 __clean_env(true);
394 errno = savedErrno;
396 return (-1);
401 * Destructor function with default argument to __clean_env().
403 static void
404 __clean_env_destructor(void)
406 __clean_env(true);
408 return;
413 * Returns the value of a variable or NULL if none are found.
415 char *
416 getenv(const char *name)
418 int envNdx;
419 size_t nameLen;
421 /* Check for malformed name. */
422 if (name == NULL || (nameLen = __strleneq(name)) == 0) {
423 errno = EINVAL;
424 return (NULL);
428 * An empty environment (environ or its first value) regardless if
429 * environ has been copied before will return a NULL.
431 * If the environment is not empty, find an environment variable via
432 * environ if environ has not been copied via an *env() call or been
433 * replaced by a running program, otherwise, use the rebuilt
434 * environment.
436 if (environ == NULL || environ[0] == NULL)
437 return (NULL);
438 else if (envVars == NULL || environ != intEnviron)
439 return (__findenv_environ(name, nameLen));
440 else {
441 envNdx = envVarsTotal - 1;
442 return (__findenv(name, nameLen, &envNdx, true));
448 * Set the value of a variable. Older settings are labeled as inactive. If an
449 * older setting has enough room to store the new value, it will be reused. No
450 * previous variables are ever freed here to avoid causing a segmentation fault
451 * in a user's code.
453 * The variables nameLen and valueLen are passed into here to allow the caller
454 * to calculate the length by means besides just strlen().
456 static int
457 __setenv(const char *name, size_t nameLen, const char *value, int overwrite)
459 bool reuse;
460 char *env;
461 int envNdx;
462 int newEnvActive;
463 size_t valueLen;
465 /* Find existing environment variable large enough to use. */
466 envNdx = envVarsTotal - 1;
467 newEnvActive = envActive;
468 valueLen = strlen(value);
469 reuse = false;
470 if (__findenv(name, nameLen, &envNdx, false) != NULL) {
471 /* Deactivate entry if overwrite is allowed. */
472 if (envVars[envNdx].active) {
473 if (overwrite == 0)
474 return (0);
475 envVars[envNdx].active = false;
476 newEnvActive--;
479 /* putenv() created variable cannot be reused. */
480 if (envVars[envNdx].putenv)
481 __remove_putenv(envNdx);
483 /* Entry is large enough to reuse. */
484 else if (envVars[envNdx].valueSize >= valueLen)
485 reuse = true;
488 /* Create new variable if none was found of sufficient size. */
489 if (! reuse) {
490 /* Enlarge environment. */
491 envNdx = envVarsTotal;
492 if (!__enlarge_env())
493 return (-1);
495 /* Create environment entry. */
496 envVars[envNdx].name = malloc(nameLen + sizeof ("=") +
497 valueLen);
498 if (envVars[envNdx].name == NULL) {
499 envVarsTotal--;
500 return (-1);
502 envVars[envNdx].nameLen = nameLen;
503 envVars[envNdx].valueSize = valueLen;
505 /* Save name of name/value pair. */
506 env = stpcpy(envVars[envNdx].name, name);
507 if ((envVars[envNdx].name)[nameLen] != '=')
508 env = stpcpy(env, "=");
510 else
511 env = envVars[envNdx].value;
513 /* Save value of name/value pair. */
514 strcpy(env, value);
515 envVars[envNdx].value = env;
516 envVars[envNdx].active = true;
517 newEnvActive++;
519 /* No need to rebuild environ if an active variable was reused. */
520 if (reuse && newEnvActive == envActive)
521 return (0);
522 else
523 return (__rebuild_environ(newEnvActive));
528 * If the program attempts to replace the array of environment variables
529 * (environ) environ or sets the first varible to NULL, then deactivate all
530 * variables and merge in the new list from environ.
532 static int
533 __merge_environ(void)
535 char **env;
536 char *equals;
539 * Internally-built environ has been replaced or cleared (detected by
540 * using the count of active variables against a NULL as the first value
541 * in environ). Clean up everything.
543 if (intEnviron != NULL && (environ != intEnviron || (envActive > 0 &&
544 environ[0] == NULL))) {
545 /* Deactivate all environment variables. */
546 if (envActive > 0) {
547 origEnviron = NULL;
548 __clean_env(false);
552 * Insert new environ into existing, yet deactivated,
553 * environment array.
555 origEnviron = environ;
556 if (origEnviron != NULL)
557 for (env = origEnviron; *env != NULL; env++) {
558 if ((equals = strchr(*env, '=')) == NULL) {
559 __env_warnx(CorruptEnvValueMsg, *env,
560 strlen(*env));
561 errno = EFAULT;
562 return (-1);
564 if (__setenv(*env, equals - *env, equals + 1,
565 1) == -1)
566 return (-1);
570 return (0);
575 * The exposed setenv() that peforms a few tests before calling the function
576 * (__setenv()) that does the actual work of inserting a variable into the
577 * environment.
580 setenv(const char *name, const char *value, int overwrite)
582 size_t nameLen;
584 /* Check for malformed name. */
585 if (name == NULL || (nameLen = __strleneq(name)) == 0) {
586 errno = EINVAL;
587 return (-1);
590 /* Initialize environment. */
591 if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))
592 return (-1);
594 return (__setenv(name, nameLen, value, overwrite));
599 * Insert a "name=value" string into the environment. Special settings must be
600 * made to keep setenv() from reusing this memory block and unsetenv() from
601 * allowing it to be tracked.
604 putenv(char *string)
606 char *equals;
607 int envNdx;
608 int newEnvActive;
609 size_t nameLen;
611 /* Check for malformed argument. */
612 if (string == NULL || (equals = strchr(string, '=')) == NULL ||
613 (nameLen = equals - string) == 0) {
614 errno = EINVAL;
615 return (-1);
618 /* Initialize environment. */
619 if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))
620 return (-1);
622 /* Deactivate previous environment variable. */
623 envNdx = envVarsTotal - 1;
624 newEnvActive = envActive;
625 if (__findenv(string, nameLen, &envNdx, true) != NULL) {
626 /* Reuse previous putenv slot. */
627 if (envVars[envNdx].putenv) {
628 envVars[envNdx].name = string;
629 return (__rebuild_environ(envActive));
630 } else {
631 newEnvActive--;
632 envVars[envNdx].active = false;
636 /* Enlarge environment. */
637 envNdx = envVarsTotal;
638 if (!__enlarge_env())
639 return (-1);
641 /* Create environment entry. */
642 envVars[envNdx].name = string;
643 envVars[envNdx].nameLen = -1;
644 envVars[envNdx].value = NULL;
645 envVars[envNdx].valueSize = -1;
646 envVars[envNdx].putenv = true;
647 envVars[envNdx].active = true;
648 newEnvActive++;
650 return (__rebuild_environ(newEnvActive));
655 * Unset variable with the same name by flagging it as inactive. No variable is
656 * ever freed.
659 unsetenv(const char *name)
661 int envNdx;
662 size_t nameLen;
664 /* Check for malformed name. */
665 if (name == NULL || (nameLen = __strleneq(name)) == 0) {
666 errno = EINVAL;
667 return (-1);
670 /* Initialize environment. */
671 if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))
672 return (-1);
674 /* Deactivate specified variable. */
675 envNdx = envVarsTotal - 1;
676 if (__findenv(name, nameLen, &envNdx, true) != NULL) {
677 envVars[envNdx].active = false;
678 if (envVars[envNdx].putenv)
679 __remove_putenv(envNdx);
680 __rebuild_environ(envActive - 1);
683 return (0);