1171195Sscf/*-
2200190Sscf * Copyright (c) 2007-2009 Sean C. Farley <scf@FreeBSD.org>
3171195Sscf * All rights reserved.
41573Srgrimes *
51573Srgrimes * Redistribution and use in source and binary forms, with or without
61573Srgrimes * modification, are permitted provided that the following conditions
71573Srgrimes * are met:
81573Srgrimes * 1. Redistributions of source code must retain the above copyright
9171195Sscf *    notice, this list of conditions and the following disclaimer,
10171195Sscf *    without modification, immediately at the beginning of the file.
111573Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
121573Srgrimes *    notice, this list of conditions and the following disclaimer in the
131573Srgrimes *    documentation and/or other materials provided with the distribution.
141573Srgrimes *
15171195Sscf * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16171195Sscf * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17171195Sscf * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18171195Sscf * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19171195Sscf * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20171195Sscf * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21171195Sscf * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22171195Sscf * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23171195Sscf * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24171195Sscf * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
251573Srgrimes */
26176632Sscf
27176632Sscf#include <sys/cdefs.h>
28176632Sscf__FBSDID("$FreeBSD: stable/10/lib/libc/stdlib/getenv.c 315415 2017-03-16 15:10:04Z pfg $");
29176632Sscf
30176632Sscf
31176632Sscf#include "namespace.h"
32171195Sscf#include <sys/types.h>
33171195Sscf#include <errno.h>
34171195Sscf#include <stdbool.h>
35171195Sscf#include <stddef.h>
36171195Sscf#include <stdlib.h>
37171195Sscf#include <string.h>
38176632Sscf#include <unistd.h>
39176632Sscf#include "un-namespace.h"
401573Srgrimes
41171195Sscf
42176632Sscfstatic const char CorruptEnvFindMsg[] = "environment corrupt; unable to find ";
43171525Sscfstatic const char CorruptEnvValueMsg[] =
44176632Sscf    "environment corrupt; missing value for ";
45171525Sscf
46171525Sscf
47171195Sscf/*
48171195Sscf * Standard environ.  environ variable is exposed to entire process.
49171195Sscf *
50171195Sscf *	origEnviron:	Upon cleanup on unloading of library or failure, this
51171195Sscf *			allows environ to return to as it was before.
52171195Sscf *	environSize:	Number of variables environ can hold.  Can only
53171195Sscf *			increase.
54171525Sscf *	intEnviron:	Internally-built environ.  Exposed via environ during
55171525Sscf *			(re)builds of the environment.
56171195Sscf */
57171195Sscfextern char **environ;
58171195Sscfstatic char **origEnviron;
59171525Sscfstatic char **intEnviron = NULL;
60171195Sscfstatic int environSize = 0;
611573Srgrimes
621573Srgrimes/*
63171195Sscf * Array of environment variables built from environ.  Each element records:
64171195Sscf *	name:		Pointer to name=value string
65171195Sscf *	name length:	Length of name not counting '=' character
66171195Sscf *	value:		Pointer to value within same string as name
67171195Sscf *	value size:	Size (not length) of space for value not counting the
68171195Sscf *			nul character
69171195Sscf *	active state:	true/false value to signify whether variable is active.
70171195Sscf *			Useful since multiple variables with the same name can
71171195Sscf *			co-exist.  At most, one variable can be active at any
72171195Sscf *			one time.
73171195Sscf *	putenv:		Created from putenv() call.  This memory must not be
74171195Sscf *			reused.
75171195Sscf */
76171195Sscfstatic struct envVars {
77171195Sscf	size_t nameLen;
78171195Sscf	size_t valueSize;
79171195Sscf	char *name;
80171195Sscf	char *value;
81171195Sscf	bool active;
82171195Sscf	bool putenv;
83171195Sscf} *envVars = NULL;
84171195Sscf
85171195Sscf/*
86171195Sscf * Environment array information.
871573Srgrimes *
88171195Sscf *	envActive:	Number of active variables in array.
89171195Sscf *	envVarsSize:	Size of array.
90171195Sscf *	envVarsTotal:	Number of total variables in array (active or not).
911573Srgrimes */
92171195Sscfstatic int envActive = 0;
93171195Sscfstatic int envVarsSize = 0;
94171195Sscfstatic int envVarsTotal = 0;
95171195Sscf
96171195Sscf
97171195Sscf/* Deinitialization of new environment. */
98171525Sscfstatic void __attribute__ ((destructor)) __clean_env_destructor(void);
99171195Sscf
100171195Sscf
101171195Sscf/*
102176632Sscf * A simple version of warnx() to avoid the bloat of including stdio in static
103176632Sscf * binaries.
104176632Sscf */
105176632Sscfstatic void
106176632Sscf__env_warnx(const char *msg, const char *name, size_t nameLen)
107176632Sscf{
108176632Sscf	static const char nl[] = "\n";
109176632Sscf	static const char progSep[] = ": ";
110176632Sscf
111176632Sscf	_write(STDERR_FILENO, _getprogname(), strlen(_getprogname()));
112176632Sscf	_write(STDERR_FILENO, progSep, sizeof(progSep) - 1);
113176632Sscf	_write(STDERR_FILENO, msg, strlen(msg));
114176632Sscf	_write(STDERR_FILENO, name, nameLen);
115176632Sscf	_write(STDERR_FILENO, nl, sizeof(nl) - 1);
116176632Sscf
117176632Sscf	return;
118176632Sscf}
119176632Sscf
120176632Sscf
121176632Sscf/*
122171195Sscf * Inline strlen() for performance.  Also, perform check for an equals sign.
123171195Sscf * Cheaper here than peforming a strchr() later.
124171195Sscf */
125171195Sscfstatic inline size_t
126171195Sscf__strleneq(const char *str)
1271573Srgrimes{
128171195Sscf	const char *s;
1291573Srgrimes
130171195Sscf	for (s = str; *s != '\0'; ++s)
131171195Sscf		if (*s == '=')
132171195Sscf			return (0);
133171195Sscf
134171195Sscf	return (s - str);
135171195Sscf}
136171195Sscf
137171195Sscf
138171195Sscf/*
139171195Sscf * Comparison of an environment name=value to a name.
140171195Sscf */
141171195Sscfstatic inline bool
142171195Sscfstrncmpeq(const char *nameValue, const char *name, size_t nameLen)
143171195Sscf{
144171195Sscf	if (strncmp(nameValue, name, nameLen) == 0 && nameValue[nameLen] == '=')
145171195Sscf		return (true);
146171195Sscf
147171195Sscf	return (false);
148171195Sscf}
149171195Sscf
150171195Sscf
151171195Sscf/*
152171195Sscf * Using environment, returns pointer to value associated with name, if any,
153171195Sscf * else NULL.  If the onlyActive flag is set to true, only variables that are
154171195Sscf * active are returned else all are.
155171195Sscf */
156171195Sscfstatic inline char *
157171195Sscf__findenv(const char *name, size_t nameLen, int *envNdx, bool onlyActive)
158171195Sscf{
159171195Sscf	int ndx;
160171195Sscf
161171195Sscf	/*
162171195Sscf	 * Find environment variable from end of array (more likely to be
163200190Sscf	 * active).  A variable created by putenv is always active, or it is not
164171195Sscf	 * tracked in the array.
165171195Sscf	 */
166171195Sscf	for (ndx = *envNdx; ndx >= 0; ndx--)
167171195Sscf		if (envVars[ndx].putenv) {
168171195Sscf			if (strncmpeq(envVars[ndx].name, name, nameLen)) {
169171195Sscf				*envNdx = ndx;
170171195Sscf				return (envVars[ndx].name + nameLen +
171171195Sscf				    sizeof ("=") - 1);
172171195Sscf			}
173171195Sscf		} else if ((!onlyActive || envVars[ndx].active) &&
174171195Sscf		    (envVars[ndx].nameLen == nameLen &&
175171195Sscf		    strncmpeq(envVars[ndx].name, name, nameLen))) {
176171195Sscf			*envNdx = ndx;
177171195Sscf			return (envVars[ndx].value);
178171195Sscf		}
179171195Sscf
180171195Sscf	return (NULL);
181171195Sscf}
182171195Sscf
183171195Sscf
184171195Sscf/*
185171195Sscf * Using environ, returns pointer to value associated with name, if any, else
186171195Sscf * NULL.  Used on the original environ passed into the program.
187171195Sscf */
188171195Sscfstatic char *
189171195Sscf__findenv_environ(const char *name, size_t nameLen)
190171195Sscf{
191171195Sscf	int envNdx;
192171195Sscf
193171195Sscf	/* Find variable within environ. */
194171195Sscf	for (envNdx = 0; environ[envNdx] != NULL; envNdx++)
195171195Sscf		if (strncmpeq(environ[envNdx], name, nameLen))
196171195Sscf			return (&(environ[envNdx][nameLen + sizeof("=") - 1]));
197171195Sscf
198171195Sscf	return (NULL);
199171195Sscf}
200171195Sscf
201171195Sscf
202171195Sscf/*
203171525Sscf * Remove variable added by putenv() from variable tracking array.
204171525Sscf */
205171525Sscfstatic void
206171525Sscf__remove_putenv(int envNdx)
207171525Sscf{
208171525Sscf	envVarsTotal--;
209171525Sscf	if (envVarsTotal > envNdx)
210171525Sscf		memmove(&(envVars[envNdx]), &(envVars[envNdx + 1]),
211171525Sscf		    (envVarsTotal - envNdx) * sizeof (*envVars));
212171525Sscf	memset(&(envVars[envVarsTotal]), 0, sizeof (*envVars));
213171525Sscf
214171525Sscf	return;
215171525Sscf}
216171525Sscf
217171525Sscf
218171525Sscf/*
219171525Sscf * Deallocate the environment built from environ as well as environ then set
220171525Sscf * both to NULL.  Eases debugging of memory leaks.
221171525Sscf */
222171525Sscfstatic void
223171525Sscf__clean_env(bool freeVars)
224171525Sscf{
225171525Sscf	int envNdx;
226171525Sscf
227171525Sscf	/* Deallocate environment and environ if created by *env(). */
228171525Sscf	if (envVars != NULL) {
229171525Sscf		for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--)
230171525Sscf			/* Free variables or deactivate them. */
231171525Sscf			if (envVars[envNdx].putenv) {
232171525Sscf				if (!freeVars)
233171525Sscf					__remove_putenv(envNdx);
234171525Sscf			} else {
235171525Sscf				if (freeVars)
236171525Sscf					free(envVars[envNdx].name);
237171525Sscf				else
238171525Sscf					envVars[envNdx].active = false;
239171525Sscf			}
240171525Sscf		if (freeVars) {
241171525Sscf			free(envVars);
242171525Sscf			envVars = NULL;
243171525Sscf		} else
244171525Sscf			envActive = 0;
245171525Sscf
246171525Sscf		/* Restore original environ if it has not updated by program. */
247171525Sscf		if (origEnviron != NULL) {
248171525Sscf			if (environ == intEnviron)
249171525Sscf				environ = origEnviron;
250171525Sscf			free(intEnviron);
251171525Sscf			intEnviron = NULL;
252171525Sscf			environSize = 0;
253171525Sscf		}
254171525Sscf	}
255171525Sscf
256171525Sscf	return;
257171525Sscf}
258171525Sscf
259171525Sscf
260171525Sscf/*
261171195Sscf * Using the environment, rebuild the environ array for use by other C library
262171195Sscf * calls that depend upon it.
263171195Sscf */
264171195Sscfstatic int
265171195Sscf__rebuild_environ(int newEnvironSize)
266171195Sscf{
267171195Sscf	char **tmpEnviron;
268171195Sscf	int envNdx;
269171195Sscf	int environNdx;
270171195Sscf	int tmpEnvironSize;
271171195Sscf
272171195Sscf	/* Resize environ. */
273171195Sscf	if (newEnvironSize > environSize) {
274171195Sscf		tmpEnvironSize = newEnvironSize * 2;
275171525Sscf		tmpEnviron = realloc(intEnviron, sizeof (*intEnviron) *
276171195Sscf		    (tmpEnvironSize + 1));
277171195Sscf		if (tmpEnviron == NULL)
278171195Sscf			return (-1);
279171195Sscf		environSize = tmpEnvironSize;
280171525Sscf		intEnviron = tmpEnviron;
281171195Sscf	}
282171195Sscf	envActive = newEnvironSize;
283171195Sscf
284171195Sscf	/* Assign active variables to environ. */
285171195Sscf	for (envNdx = envVarsTotal - 1, environNdx = 0; envNdx >= 0; envNdx--)
286171195Sscf		if (envVars[envNdx].active)
287171525Sscf			intEnviron[environNdx++] = envVars[envNdx].name;
288171525Sscf	intEnviron[environNdx] = NULL;
289171195Sscf
290171525Sscf	/* Always set environ which may have been replaced by program. */
291171525Sscf	environ = intEnviron;
292171525Sscf
293171195Sscf	return (0);
294171195Sscf}
295171195Sscf
296171195Sscf
297171195Sscf/*
298171195Sscf * Enlarge new environment.
299171195Sscf */
300171195Sscfstatic inline bool
301171195Sscf__enlarge_env(void)
302171195Sscf{
303171195Sscf	int newEnvVarsSize;
304171195Sscf	struct envVars *tmpEnvVars;
305171195Sscf
306171195Sscf	envVarsTotal++;
307171195Sscf	if (envVarsTotal > envVarsSize) {
308171195Sscf		newEnvVarsSize = envVarsTotal * 2;
309171195Sscf		tmpEnvVars = realloc(envVars, sizeof (*envVars) *
310171195Sscf		    newEnvVarsSize);
311171195Sscf		if (tmpEnvVars == NULL) {
312171195Sscf			envVarsTotal--;
313171195Sscf			return (false);
3141573Srgrimes		}
315171195Sscf		envVarsSize = newEnvVarsSize;
316171195Sscf		envVars = tmpEnvVars;
31711549Sdg	}
318171195Sscf
319171195Sscf	return (true);
3201573Srgrimes}
32111549Sdg
322171195Sscf
32311549Sdg/*
324171195Sscf * Using environ, build an environment for use by standard C library calls.
32511549Sdg */
326171195Sscfstatic int
327171195Sscf__build_env(void)
328171195Sscf{
329171195Sscf	char **env;
330171195Sscf	int activeNdx;
331171195Sscf	int envNdx;
332171195Sscf	int savedErrno;
333171195Sscf	size_t nameLen;
334171195Sscf
335171195Sscf	/* Check for non-existant environment. */
336171525Sscf	if (environ == NULL || environ[0] == NULL)
337171195Sscf		return (0);
338171195Sscf
339171195Sscf	/* Count environment variables. */
340171195Sscf	for (env = environ, envVarsTotal = 0; *env != NULL; env++)
341171195Sscf		envVarsTotal++;
342171195Sscf	envVarsSize = envVarsTotal * 2;
343171195Sscf
344171195Sscf	/* Create new environment. */
345315415Spfg	envVars = calloc(envVarsSize, sizeof(*envVars));
346171195Sscf	if (envVars == NULL)
347171195Sscf		goto Failure;
348171195Sscf
349171195Sscf	/* Copy environ values and keep track of them. */
350171195Sscf	for (envNdx = envVarsTotal - 1; envNdx >= 0; envNdx--) {
351171195Sscf		envVars[envNdx].putenv = false;
352171195Sscf		envVars[envNdx].name =
353171195Sscf		    strdup(environ[envVarsTotal - envNdx - 1]);
354171195Sscf		if (envVars[envNdx].name == NULL)
355171195Sscf			goto Failure;
356199987Sgreen		envVars[envNdx].value = strchr(envVars[envNdx].name, '=');
357199987Sgreen		if (envVars[envNdx].value != NULL) {
358199987Sgreen			envVars[envNdx].value++;
359199987Sgreen			envVars[envNdx].valueSize =
360199987Sgreen			    strlen(envVars[envNdx].value);
361199987Sgreen		} else {
362199987Sgreen			__env_warnx(CorruptEnvValueMsg, envVars[envNdx].name,
363199987Sgreen			    strlen(envVars[envNdx].name));
364200198Sscf			errno = EFAULT;
365200198Sscf			goto Failure;
366199987Sgreen		}
367199987Sgreen
368171195Sscf		/*
369171195Sscf		 * Find most current version of variable to make active.  This
370171195Sscf		 * will prevent multiple active variables from being created
371171195Sscf		 * during this initialization phase.
372171195Sscf		 */
373171195Sscf		nameLen = envVars[envNdx].value - envVars[envNdx].name - 1;
374171195Sscf		envVars[envNdx].nameLen = nameLen;
375171195Sscf		activeNdx = envVarsTotal - 1;
376171195Sscf		if (__findenv(envVars[envNdx].name, nameLen, &activeNdx,
377171195Sscf		    false) == NULL) {
378176632Sscf			__env_warnx(CorruptEnvFindMsg, envVars[envNdx].name,
379176632Sscf			    nameLen);
380200198Sscf			errno = EFAULT;
381200198Sscf			goto Failure;
382171195Sscf		}
383171195Sscf		envVars[activeNdx].active = true;
384171195Sscf	}
385171195Sscf
386171195Sscf	/* Create a new environ. */
387171195Sscf	origEnviron = environ;
388171195Sscf	environ = NULL;
389171525Sscf	if (__rebuild_environ(envVarsTotal) == 0)
390171525Sscf		return (0);
391171195Sscf
392171195SscfFailure:
393171195Sscf	savedErrno = errno;
394171525Sscf	__clean_env(true);
395171195Sscf	errno = savedErrno;
396171195Sscf
397171195Sscf	return (-1);
398171195Sscf}
399171195Sscf
400171195Sscf
401171195Sscf/*
402171525Sscf * Destructor function with default argument to __clean_env().
403171195Sscf */
404171195Sscfstatic void
405171525Sscf__clean_env_destructor(void)
406171195Sscf{
407171525Sscf	__clean_env(true);
408171195Sscf
409171195Sscf	return;
410171195Sscf}
411171195Sscf
412171195Sscf
413171195Sscf/*
414171195Sscf * Returns the value of a variable or NULL if none are found.
415171195Sscf */
41611549Sdgchar *
417171195Sscfgetenv(const char *name)
41811549Sdg{
419171195Sscf	int envNdx;
420171195Sscf	size_t nameLen;
42111549Sdg
422171195Sscf	/* Check for malformed name. */
423171195Sscf	if (name == NULL || (nameLen = __strleneq(name)) == 0) {
424171195Sscf		errno = EINVAL;
425171195Sscf		return (NULL);
426171195Sscf	}
427171195Sscf
428171525Sscf	/*
429200190Sscf	 * Variable search order:
430200190Sscf	 * 1. Check for an empty environ.  This allows an application to clear
431200190Sscf	 *    the environment.
432200190Sscf	 * 2. Search the external environ array.
433200190Sscf	 * 3. Search the internal environment.
434199987Sgreen	 *
435200190Sscf	 * Since malloc() depends upon getenv(), getenv() must never cause the
436200190Sscf	 * internal environment storage to be generated.
437171525Sscf	 */
438199987Sgreen	if (environ == NULL || environ[0] == NULL)
439199987Sgreen		return (NULL);
440199987Sgreen	else if (envVars == NULL || environ != intEnviron)
441199983Sgreen		return (__findenv_environ(name, nameLen));
442199987Sgreen	else {
443199987Sgreen		envNdx = envVarsTotal - 1;
444199987Sgreen		return (__findenv(name, nameLen, &envNdx, true));
445199987Sgreen	}
44611549Sdg}
447171195Sscf
448171195Sscf
449171195Sscf/*
450171195Sscf * Set the value of a variable.  Older settings are labeled as inactive.  If an
451171195Sscf * older setting has enough room to store the new value, it will be reused.  No
452171195Sscf * previous variables are ever freed here to avoid causing a segmentation fault
453171195Sscf * in a user's code.
454171525Sscf *
455171525Sscf * The variables nameLen and valueLen are passed into here to allow the caller
456171525Sscf * to calculate the length by means besides just strlen().
457171195Sscf */
458171525Sscfstatic int
459171525Sscf__setenv(const char *name, size_t nameLen, const char *value, int overwrite)
460171195Sscf{
461171195Sscf	bool reuse;
462171195Sscf	char *env;
463171195Sscf	int envNdx;
464171195Sscf	int newEnvActive;
465171195Sscf	size_t valueLen;
466171195Sscf
467171195Sscf	/* Find existing environment variable large enough to use. */
468171195Sscf	envNdx = envVarsTotal - 1;
469171195Sscf	newEnvActive = envActive;
470171195Sscf	valueLen = strlen(value);
471171195Sscf	reuse = false;
472171195Sscf	if (__findenv(name, nameLen, &envNdx, false) != NULL) {
473171195Sscf		/* Deactivate entry if overwrite is allowed. */
474171195Sscf		if (envVars[envNdx].active) {
475171195Sscf			if (overwrite == 0)
476171195Sscf				return (0);
477171195Sscf			envVars[envNdx].active = false;
478171195Sscf			newEnvActive--;
479171195Sscf		}
480171195Sscf
481171195Sscf		/* putenv() created variable cannot be reused. */
482171195Sscf		if (envVars[envNdx].putenv)
483171195Sscf			__remove_putenv(envNdx);
484171195Sscf
485171195Sscf		/* Entry is large enough to reuse. */
486171195Sscf		else if (envVars[envNdx].valueSize >= valueLen)
487171195Sscf			reuse = true;
488171195Sscf	}
489171195Sscf
490171195Sscf	/* Create new variable if none was found of sufficient size. */
491171195Sscf	if (! reuse) {
492171195Sscf		/* Enlarge environment. */
493171195Sscf		envNdx = envVarsTotal;
494171195Sscf		if (!__enlarge_env())
495171195Sscf			return (-1);
496171195Sscf
497171195Sscf		/* Create environment entry. */
498171195Sscf		envVars[envNdx].name = malloc(nameLen + sizeof ("=") +
499171195Sscf		    valueLen);
500171195Sscf		if (envVars[envNdx].name == NULL) {
501171195Sscf			envVarsTotal--;
502171195Sscf			return (-1);
503171195Sscf		}
504171195Sscf		envVars[envNdx].nameLen = nameLen;
505171195Sscf		envVars[envNdx].valueSize = valueLen;
506171195Sscf
507171195Sscf		/* Save name of name/value pair. */
508253380Savg		env = stpncpy(envVars[envNdx].name, name, nameLen);
509253413Savg		*env++ = '=';
510171195Sscf	}
511171195Sscf	else
512171195Sscf		env = envVars[envNdx].value;
513171195Sscf
514171195Sscf	/* Save value of name/value pair. */
515171195Sscf	strcpy(env, value);
516171195Sscf	envVars[envNdx].value = env;
517171195Sscf	envVars[envNdx].active = true;
518171195Sscf	newEnvActive++;
519171195Sscf
520172191Sscf	/* No need to rebuild environ if an active variable was reused. */
521172191Sscf	if (reuse && newEnvActive == envActive)
522171195Sscf		return (0);
523171195Sscf	else
524171195Sscf		return (__rebuild_environ(newEnvActive));
525171195Sscf}
526171195Sscf
527171195Sscf
528171195Sscf/*
529171525Sscf * If the program attempts to replace the array of environment variables
530181150Sscf * (environ) environ or sets the first varible to NULL, then deactivate all
531181150Sscf * variables and merge in the new list from environ.
532171525Sscf */
533171525Sscfstatic int
534171525Sscf__merge_environ(void)
535171525Sscf{
536171525Sscf	char **env;
537171525Sscf	char *equals;
538171525Sscf
539181150Sscf	/*
540181266Sscf	 * Internally-built environ has been replaced or cleared (detected by
541181266Sscf	 * using the count of active variables against a NULL as the first value
542181266Sscf	 * in environ).  Clean up everything.
543181150Sscf	 */
544181266Sscf	if (intEnviron != NULL && (environ != intEnviron || (envActive > 0 &&
545181266Sscf	    environ[0] == NULL))) {
546171525Sscf		/* Deactivate all environment variables. */
547171525Sscf		if (envActive > 0) {
548171525Sscf			origEnviron = NULL;
549171525Sscf			__clean_env(false);
550171525Sscf		}
551171525Sscf
552171525Sscf		/*
553171525Sscf		 * Insert new environ into existing, yet deactivated,
554171525Sscf		 * environment array.
555171525Sscf		 */
556171525Sscf		origEnviron = environ;
557171525Sscf		if (origEnviron != NULL)
558171525Sscf			for (env = origEnviron; *env != NULL; env++) {
559171525Sscf				if ((equals = strchr(*env, '=')) == NULL) {
560176632Sscf					__env_warnx(CorruptEnvValueMsg, *env,
561176632Sscf					    strlen(*env));
562200198Sscf					errno = EFAULT;
563200198Sscf					return (-1);
564171525Sscf				}
565171525Sscf				if (__setenv(*env, equals - *env, equals + 1,
566171525Sscf				    1) == -1)
567171525Sscf					return (-1);
568171525Sscf			}
569171525Sscf	}
570171525Sscf
571171525Sscf	return (0);
572171525Sscf}
573171525Sscf
574171525Sscf
575171525Sscf/*
576171525Sscf * The exposed setenv() that peforms a few tests before calling the function
577171525Sscf * (__setenv()) that does the actual work of inserting a variable into the
578171525Sscf * environment.
579171525Sscf */
580171525Sscfint
581171525Sscfsetenv(const char *name, const char *value, int overwrite)
582171525Sscf{
583171525Sscf	size_t nameLen;
584171525Sscf
585171525Sscf	/* Check for malformed name. */
586171525Sscf	if (name == NULL || (nameLen = __strleneq(name)) == 0) {
587171525Sscf		errno = EINVAL;
588171525Sscf		return (-1);
589171525Sscf	}
590171525Sscf
591171525Sscf	/* Initialize environment. */
592171525Sscf	if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))
593171525Sscf		return (-1);
594171525Sscf
595171525Sscf	return (__setenv(name, nameLen, value, overwrite));
596171525Sscf}
597171525Sscf
598171525Sscf
599171525Sscf/*
600171525Sscf * Insert a "name=value" string into the environment.  Special settings must be
601171195Sscf * made to keep setenv() from reusing this memory block and unsetenv() from
602171195Sscf * allowing it to be tracked.
603171195Sscf */
604171195Sscfint
605171195Sscfputenv(char *string)
606171195Sscf{
607171195Sscf	char *equals;
608171195Sscf	int envNdx;
609171195Sscf	int newEnvActive;
610171195Sscf	size_t nameLen;
611171195Sscf
612171195Sscf	/* Check for malformed argument. */
613171195Sscf	if (string == NULL || (equals = strchr(string, '=')) == NULL ||
614171195Sscf	    (nameLen = equals - string) == 0) {
615171195Sscf		errno = EINVAL;
616171195Sscf		return (-1);
617171195Sscf	}
618171195Sscf
619171195Sscf	/* Initialize environment. */
620171525Sscf	if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))
621171195Sscf		return (-1);
622171195Sscf
623171195Sscf	/* Deactivate previous environment variable. */
624171195Sscf	envNdx = envVarsTotal - 1;
625171195Sscf	newEnvActive = envActive;
626171195Sscf	if (__findenv(string, nameLen, &envNdx, true) != NULL) {
627171195Sscf		/* Reuse previous putenv slot. */
628171195Sscf		if (envVars[envNdx].putenv) {
629171195Sscf			envVars[envNdx].name = string;
630171195Sscf			return (__rebuild_environ(envActive));
631171195Sscf		} else {
632171195Sscf			newEnvActive--;
633171195Sscf			envVars[envNdx].active = false;
634171195Sscf		}
635171195Sscf	}
636171195Sscf
637171195Sscf	/* Enlarge environment. */
638171195Sscf	envNdx = envVarsTotal;
639171195Sscf	if (!__enlarge_env())
640171195Sscf		return (-1);
641171195Sscf
642171195Sscf	/* Create environment entry. */
643171195Sscf	envVars[envNdx].name = string;
644171195Sscf	envVars[envNdx].nameLen = -1;
645171195Sscf	envVars[envNdx].value = NULL;
646171195Sscf	envVars[envNdx].valueSize = -1;
647171195Sscf	envVars[envNdx].putenv = true;
648171195Sscf	envVars[envNdx].active = true;
649171195Sscf	newEnvActive++;
650171195Sscf
651171195Sscf	return (__rebuild_environ(newEnvActive));
652171195Sscf}
653171195Sscf
654171195Sscf
655171195Sscf/*
656171195Sscf * Unset variable with the same name by flagging it as inactive.  No variable is
657171195Sscf * ever freed.
658171195Sscf */
659171195Sscfint
660171195Sscfunsetenv(const char *name)
661171195Sscf{
662171195Sscf	int envNdx;
663171195Sscf	size_t nameLen;
664241154Sache	int newEnvActive;
665171195Sscf
666171195Sscf	/* Check for malformed name. */
667171195Sscf	if (name == NULL || (nameLen = __strleneq(name)) == 0) {
668171195Sscf		errno = EINVAL;
669171195Sscf		return (-1);
670171195Sscf	}
671171195Sscf
672171195Sscf	/* Initialize environment. */
673171525Sscf	if (__merge_environ() == -1 || (envVars == NULL && __build_env() == -1))
674171195Sscf		return (-1);
675171195Sscf
676171195Sscf	/* Deactivate specified variable. */
677241154Sache	/* Remove all occurrences. */
678171195Sscf	envNdx = envVarsTotal - 1;
679241154Sache	newEnvActive = envActive;
680241137Sache	while (__findenv(name, nameLen, &envNdx, true) != NULL) {
681171195Sscf		envVars[envNdx].active = false;
682171195Sscf		if (envVars[envNdx].putenv)
683171195Sscf			__remove_putenv(envNdx);
684241154Sache		envNdx--;
685241154Sache		newEnvActive--;
686171195Sscf	}
687241154Sache	if (newEnvActive != envActive)
688241154Sache		__rebuild_environ(newEnvActive);
689171195Sscf
690171195Sscf	return (0);
691171195Sscf}
692