1 /* Copyright (C) 1992,95,96,97,98,99,2000,2001 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; see the file COPYING.LIB.  If
16    not, see <http://www.gnu.org/licenses/>.
17 
18    modified for uClibc by Erik Andersen <andersen@codepoet.org>
19 */
20 
21 #include <features.h>
22 #include <errno.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 
27 
28 #include <bits/uClibc_mutex.h>
29 __UCLIBC_MUTEX_STATIC(mylock, PTHREAD_MUTEX_INITIALIZER);
30 
31 
32 /* If this variable is not a null pointer we allocated the current
33    environment.  */
34 static char **last_environ;
35 
36 
37 /* This function is used by `setenv' and `putenv'.  The difference between
38    the two functions is that for the former must create a new string which
39    is then placed in the environment, while the argument of `putenv'
40    must be used directly.  This is all complicated by the fact that we try
41    to reuse values once generated for a `setenv' call since we can never
42    free the strings. [in uclibc, we do not]  */
__add_to_environ(const char * name,const char * value,int replace)43 static int __add_to_environ(const char *name, const char *value,
44 		int replace)
45 {
46 	register char **ep;
47 	register size_t size;
48 	char *var_val;
49 	char **new_environ;
50 	/* name may come from putenv() and thus may contain "=VAL" part */
51 	const size_t namelen = strchrnul(name, '=') - name;
52 	int rv = -1;
53 
54 	__UCLIBC_MUTEX_LOCK(mylock);
55 
56 	/* We have to get the pointer now that we have the lock and not earlier
57 	   since another thread might have created a new environment.  */
58 	ep = __environ;
59 
60 	size = 0;
61 	if (ep != NULL) {
62 		while (*ep != NULL) {
63 			if (!strncmp(*ep, name, namelen) && (*ep)[namelen] == '=') {
64 				/* Found */
65 				if (!replace)
66 					goto DONE_OK;
67 				goto REPLACE;
68 			}
69 			++size;
70 			++ep;
71 		}
72 	}
73 
74 	/* Not found, add at the end */
75 
76 	/* We allocated this space; we can extend it.  */
77 	new_environ = realloc(last_environ, (size + 2) * sizeof(char *));
78 	if (new_environ == NULL) {
79 		/* __set_errno(ENOMEM); */
80 		goto DONE;
81 	}
82 	if (__environ != last_environ) {
83 		memcpy(new_environ, __environ, size * sizeof(char *));
84 	}
85 	last_environ = __environ = new_environ;
86 
87 	ep = &new_environ[size];
88 	/* Ensure env is NULL terminated in case malloc below fails */
89 	ep[0] = NULL;
90 	ep[1] = NULL;
91 
92  REPLACE:
93 	var_val = (char*) name;
94 	/* Build VAR=VAL if we called by setenv, not putenv.  */
95 	if (value != NULL) {
96 		const size_t vallen = strlen(value) + 1;
97 
98 		var_val = malloc(namelen + 1 + vallen);
99 		if (var_val == NULL) {
100 			/* __set_errno(ENOMEM); */
101 			goto DONE;
102 		}
103 		memcpy(var_val, name, namelen);
104 		var_val[namelen] = '=';
105 		memcpy(&var_val[namelen + 1], value, vallen);
106 	}
107 	*ep = var_val;
108 
109  DONE_OK:
110 	rv = 0;
111 
112  DONE:
113 	__UCLIBC_MUTEX_UNLOCK(mylock);
114 	return rv;
115 }
116 
setenv(const char * name,const char * value,int replace)117 int setenv(const char *name, const char *value, int replace)
118 {
119 	if (name == NULL || *name == '\0' || strchr (name, '=') != NULL) {
120 		__set_errno(EINVAL);
121 		return -1;
122 	}
123 
124 	/* NB: setenv("VAR", NULL, 1) inserts "VAR=" string */
125 	return __add_to_environ(name, value ? value : "", replace);
126 }
libc_hidden_def(setenv)127 libc_hidden_def(setenv)
128 
129 int unsetenv(const char *name)
130 {
131 	const char *eq;
132 	size_t len;
133 	char **ep;
134 
135 	if (name == NULL || *name == '\0'
136 	 || *(eq = strchrnul(name, '=')) == '='
137 	) {
138 		__set_errno(EINVAL);
139 		return -1;
140 	}
141 	len = eq - name; /* avoiding strlen this way */
142 
143 	__UCLIBC_MUTEX_LOCK(mylock);
144 	ep = __environ;
145 	/* NB: clearenv(); unsetenv("foo"); should not segfault */
146 	if (ep)	while (*ep != NULL) {
147 		if (!strncmp(*ep, name, len) && (*ep)[len] == '=') {
148 			/* Found it.  Remove this pointer by moving later ones back.  */
149 			char **dp = ep;
150 			do {
151 				dp[0] = dp[1];
152 			} while (*dp++);
153 			/* Continue the loop in case NAME appears again.  */
154 		} else {
155 			++ep;
156 		}
157 	}
158 	__UCLIBC_MUTEX_UNLOCK(mylock);
159 	return 0;
160 }
libc_hidden_def(unsetenv)161 libc_hidden_def(unsetenv)
162 
163 /* The `clearenv' was planned to be added to POSIX.1 but probably
164    never made it.  Nevertheless the POSIX.9 standard (POSIX bindings
165    for Fortran 77) requires this function.  */
166 int clearenv(void)
167 {
168 	__UCLIBC_MUTEX_LOCK(mylock);
169 	/* If we allocated this environment we can free it.
170 	 * If we did not allocate this environment, it's NULL already
171 	 * and is safe to free().  */
172 	free(last_environ);
173 	last_environ = NULL;
174 	/* Clearing environ removes the whole environment.  */
175 	__environ = NULL;
176 	__UCLIBC_MUTEX_UNLOCK(mylock);
177 	return 0;
178 }
179 
180 /* Put STRING, which is of the form "NAME=VALUE", in the environment.  */
putenv(char * string)181 int putenv(char *string)
182 {
183 	if (strchr(string, '=') != NULL) {
184 		return __add_to_environ(string, NULL, 1);
185 	}
186 	return unsetenv(string);
187 }
188