#include #include #include #include #include #include #include #include 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 &get_vector() { static frg::vector 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(string); }else{ // Last pointer of environ must always be a null delimiter. __ensure(!vector.back()); vector.back() = const_cast(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(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 */