aboutsummaryrefslogtreecommitdiff
path: root/lib/mlibc/options/ansi/generic/environment.cpp
blob: 5625592027291b2489815eba7d2b22fecfe7f52e (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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>

#include <bits/ensure.h>
#include <mlibc/allocator.hpp>
#include <mlibc/debug.hpp>

#include <frg/string.hpp>
#include <frg/vector.hpp>

namespace {
	char *empty_environment[] = { nullptr };
}

char **environ = empty_environment;

namespace {

size_t find_environ_index(frg::string_view name) {
	for(size_t i = 0; environ[i]; i++) {
		frg::string_view view{environ[i]};
		size_t s = view.find_first('=');
		if(s == size_t(-1)) {
			mlibc::infoLogger() << "mlibc: environment string \""
					<< frg::escape_fmt{view.data(), view.size()}
					<< "\" does not contain an equals sign (=)" << frg::endlog;
			continue;
		}
		if(view.sub_string(0, s) == name)
			return i;
	}

	return -1;
}

// Environment vector that is mutated by putenv() and setenv().
// Cannot be global as it is accessed during library initialization.
frg::vector<char *, MemoryAllocator> &get_vector() {
	static frg::vector<char *, MemoryAllocator> vector{getAllocator()};
	return vector;
}

void update_vector() {
	auto &vector = get_vector();
	if(environ == vector.data())
		return;

	// If the environ variable was changed, we copy the environment.
	// Note that we must only copy the pointers but not the strings themselves!
	vector.clear();
	for(size_t i = 0; environ[i]; i++)
		vector.push(environ[i]);
	vector.push(nullptr);

	environ = vector.data();
}

void assign_variable(frg::string_view name, const char *string, bool overwrite) {
	auto &vector = get_vector();
	__ensure(environ == vector.data());

	auto k = find_environ_index(name);
	if(k != size_t(-1)) {
		if(overwrite)
			vector[k] = const_cast<char *>(string);
	}else{
		// Last pointer of environ must always be a null delimiter.
		__ensure(!vector.back());
		vector.back() = const_cast<char *>(string);
		vector.push(nullptr);
	}

	// push() might have re-allocated the vector.
	environ = vector.data();
}

void unassign_variable(frg::string_view name) {
	auto &vector = get_vector();
	__ensure(environ == vector.data());

	auto k = find_environ_index(name);
	if(k == size_t(-1))
		return;

	// Last pointer of environ must always be a null delimiter.
	__ensure(vector.size() >= 2 && !vector.back());
	std::swap(vector[k], vector[vector.size() - 2]);
	vector.pop();
	vector.back() = nullptr;

	// pop() might have re-allocated the vector.
	environ = vector.data();
}

} // anonymous namespace

char *getenv(const char *name) {
	auto k = find_environ_index(name);
	if(k == size_t(-1))
		return nullptr;

	frg::string_view view{environ[k]};
	size_t s = view.find_first('=');
	__ensure(s != size_t(-1));
	return const_cast<char *>(view.data() + s + 1);
}

namespace mlibc {

int putenv(char *string) {
	frg::string_view view{string};
	size_t s = view.find_first('=');
	if(s == size_t(-1))
		__ensure(!"Environment strings need to contain an equals sign");

	update_vector();
	assign_variable(view.sub_string(0, s), string, true);
	return 0;
}

} // namespace mlibc

#if __MLIBC_POSIX_OPTION

int putenv(char *string) {
	return mlibc::putenv(string);
}

int setenv(const char *name, const char *value, int overwrite) {
	frg::string_view view{name};
	size_t s = view.find_first('=');
	if(s != size_t(-1)) {
		mlibc::infoLogger() << "mlibc: environment variable \""
				<< frg::escape_fmt{view.data(), view.size()} << "\" contains an equals sign"
				<< frg::endlog;
		errno = EINVAL;
		return -1;
	}

	// We never free strings here. TODO: Reuse them?
	char *string;
	__ensure(asprintf(&string, "%s=%s", name, value) > 0);
	__ensure(string);

	update_vector();
	assign_variable(name, string, overwrite);
	return 0;
}

int unsetenv(const char *name) {
	update_vector();
	unassign_variable(name);
	return 0;
}

int clearenv(void) {
	auto vector = get_vector();
	vector.clear();
	update_vector();
	return 0;
}

#endif /* __MLIBC_POSIX_OPTION */