summaryrefslogtreecommitdiff
path: root/lib/libc/stdlib/setenv.c
blob: 1464fc1ba671e7efad1b10fb71ca85fbef28c769 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <libc.h>
#include <atomic.h>
#include <stdlib.h>
#include <string.h>

extern char **environ;

int setenv(const char *var, const char *value, int overwrite)
{
	char **env = environ;
	size_t var_len = strlen(var);

	for (; *env; env++) {
		char *eq = strchr(*env, '=');
		if (eq && ((size_t)(eq - *env) == var_len) &&
		    !strncmp(*env, var, var_len)) {
			if (overwrite) {
				size_t value_len = strlen(value);
				char *new_env =
					malloc(var_len + 1 + value_len + 1);
				if (!new_env)
					return -1;
				memcpy(new_env, var, var_len);
				new_env[var_len] = '=';
				memcpy(new_env + var_len + 1, value,
				       value_len + 1);
				*env = new_env;
			}
			return 0;
		}
	}

	size_t env_count = 0;
	while (environ[env_count])
		env_count++;

	char **new_envp = realloc(environ, (env_count + 2) * sizeof(char *));
	if (!new_envp)
		return -1;

	size_t value_len = strlen(value);
	char *new_var = malloc(var_len + 1 + value_len + 1);
	if (!new_var)
		return -1;

	memcpy(new_var, var, var_len);
	new_var[var_len] = '=';
	memcpy(new_var + var_len + 1, value, value_len + 1);

	new_envp[env_count] = new_var;
	new_envp[env_count + 1] = NULL;
	LIBC_LOCK(libc.lock.environ);
	environ = new_envp;
	LIBC_UNLOCK(libc.lock.environ);
	libc.flags |= LIBC_ENVP_TOUCHED;

	return 0;
}