diff --git a/src/env_base.cpp b/src/env_base.cpp
new file mode 100644
index 0000000..1cb8e00
--- /dev/null
+++ b/src/env_base.cpp
@@ -0,0 +1,24 @@
+/* Copyright 2020, Michele Santullo
+ * This file is part of user-gcc.
+ *
+ * User-gcc is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * User-gcc is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with user-gcc. If not, see .
+ */
+
+#include "env_base.hpp"
+
+namespace duck {
+ bool EnvBase::empty() const noexcept {
+ return 0 == this->size();
+ }
+} //namespace duck
diff --git a/src/env_base.hpp b/src/env_base.hpp
new file mode 100644
index 0000000..ad68f52
--- /dev/null
+++ b/src/env_base.hpp
@@ -0,0 +1,46 @@
+/* Copyright 2020, Michele Santullo
+ * This file is part of user-gcc.
+ *
+ * User-gcc is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * User-gcc is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with user-gcc. If not, see .
+ */
+
+#pragma once
+
+#include
+#include
+#include
+
+namespace duck {
+ class EnvBase {
+ public:
+ typedef std::string_view mapped_type;
+ typedef std::string_view key_type;
+ typedef std::pair value_type;
+ typedef std::size_t size_type;
+ typedef std::ptrdiff_t difference_type;
+
+ EnvBase() = default;
+ virtual ~EnvBase() noexcept = default;
+
+ virtual std::string_view operator[] (std::string_view name) const noexcept = 0;
+ virtual std::optional get (std::string_view name) const noexcept = 0;
+ virtual std::string_view get (std::string_view name, std::string_view def) const noexcept = 0;
+ virtual void set (std::string_view name, std::string_view value) = 0;
+ virtual void unset (std::string_view name) noexcept = 0;
+ virtual bool is_set (std::string_view name) const noexcept = 0;
+ virtual size_type size() const noexcept = 0;
+ virtual bool empty() const noexcept;
+ virtual void clear() noexcept = 0;
+ };
+} //namespace duck
diff --git a/src/env_real.cpp b/src/env_real.cpp
new file mode 100644
index 0000000..bd3903f
--- /dev/null
+++ b/src/env_real.cpp
@@ -0,0 +1,266 @@
+/* Copyright 2020, Michele Santullo
+ * This file is part of user-gcc.
+ *
+ * User-gcc is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * User-gcc is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with user-gcc. If not, see .
+ */
+
+//required for putenv()
+#define _XOPEN_SOURCE
+
+#include "env_real.hpp"
+#include "string_view_cat.hpp"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#if defined(__GNU_LIBRARY__) && ((__GLIBC__ == 2 && __GLIBC_MINOR__ >= 17) || __GLIBC__ > 2)
+# define HasSecureGetenv
+#endif
+
+namespace duck {
+
+namespace {
+ class PointerMap;
+
+ const constexpr std::size_t g_name_buff_len = 128;
+
+ PointerMap& pointer_map();
+
+ class PointerMap {
+ friend PointerMap& pointer_map();
+ public:
+ PointerMap (const PointerMap&) = delete;
+ PointerMap (PointerMap&&) = delete;
+
+ typedef std::unique_ptr pointer;
+ ~PointerMap() noexcept;
+ char* register_pointer (pointer&& ptr);
+ bool delete_if_known (const char* ptr) noexcept;
+ void clear() noexcept;
+ std::mutex& env_mutex() noexcept { return m_env_mutex; }
+ private:
+ PointerMap() = default;
+ std::mutex m_data_mutex;
+ std::mutex m_env_mutex;
+ std::unordered_map m_ptrs;;
+ };
+
+ template struct DefaultValOrVoid {
+ static T value() noexcept(noexcept(T())) {
+ return T{};
+ }
+ };
+ template <> struct DefaultValOrVoid {
+ static void value() noexcept {}
+ };
+
+ void safe_print_error (std::string_view msg) noexcept {
+ try {
+ std::cerr << msg << std::endl;
+ }
+ catch (...) {
+ }
+ }
+
+ PointerMap& pointer_map() {
+ static PointerMap pm;
+ return pm;
+ }
+
+ PointerMap::~PointerMap() noexcept {
+ std::lock_guard lock(m_data_mutex);
+ m_ptrs.clear();
+ }
+
+ char* PointerMap::register_pointer (pointer&& ptr) {
+ std::lock_guard lock(m_data_mutex);
+ char* const raw_ptr = ptr.get();
+ assert(m_ptrs.count(raw_ptr) == 0);
+ m_ptrs.insert(std::make_pair(raw_ptr, std::move(ptr)));
+ return raw_ptr;
+ }
+
+ bool PointerMap::delete_if_known (const char* ptr) noexcept {
+ if (not ptr)
+ return false;
+
+ try {
+ std::lock_guard lock(m_data_mutex);
+ auto it_found = m_ptrs.find(ptr);
+ const bool found = (m_ptrs.end() != it_found);
+ if (found)
+ m_ptrs.erase(it_found);
+ return found;
+ }
+ catch (...) {
+ safe_print_error("Exception thrown in PointerMap::delete_if_known()");
+ return false;
+ }
+ }
+
+ void PointerMap::clear() noexcept {
+ std::lock_guard lock(m_data_mutex);
+ m_ptrs.clear();
+ }
+
+ const char* raw_fetch_env (const char* name) noexcept {
+ assert(name);
+ if (not name)
+ return nullptr;
+
+#if defined(HasSecureGetenv)
+ const char* const val_ptr = secure_getenv(name);
+#else
+ const char* const val_ptr = getenv(name);
+#endif
+
+ return val_ptr;
+ }
+
+ template
+ auto invoke_with_zstr (std::string_view name, char* name_buff, std::size_t buff_size, F func) noexcept {
+ assert(not name.empty());
+ if (name.empty())
+ return DefaultValOrVoid::value();
+
+ if (name.size() < buff_size) {
+ assert(name_buff);
+ std::strncpy(name_buff, name.data(), name.size());
+ name_buff[name.size()] = '\0';
+ return func(name_buff, name.size());
+ }
+ else {
+ try {
+ auto str = to_string(name);
+ return func(str.c_str(), name.size());
+ }
+ catch (...) {
+ safe_print_error("Failed to create string for env var name");
+ return DefaultValOrVoid::value();
+ }
+ }
+ }
+} //unnamed namespace
+
+EnvReal::EnvReal() :
+ m_name_buff(std::make_unique(g_name_buff_len)),
+ m_name_buff_len(g_name_buff_len)
+{
+}
+
+std::string_view EnvReal::operator[] (std::string_view name) const noexcept {
+ return this->get(name, "");
+}
+
+std::optional EnvReal::get (std::string_view name) const noexcept {
+ const char* const ret = this->raw_fetch_env(name);
+ if (ret)
+ return to_string_view(ret);
+ else
+ return {};
+}
+
+std::string_view EnvReal::get (std::string_view name, std::string_view def) const noexcept {
+ const char* const ret = this->raw_fetch_env(name);
+ if (ret)
+ return to_string_view(ret);
+ else
+ return def;
+}
+
+void EnvReal::set (std::string_view name, std::string_view value) {
+ assert(not name.empty());
+ if (name.empty())
+ return;
+ this->unset(name);
+
+ const std::size_t new_var_size = name.size() + value.size() + 1 /* '=' */;
+ std::unique_ptr new_var(std::make_unique(new_var_size + 1));
+ std::strncpy(new_var.get(), name.data(), name.size());
+ new_var[name.size()] = '=';
+ std::strncpy(new_var.get() + name.size() + 1, value.data(), value.size());
+ new_var[new_var_size] = '\0';
+ assert(std::strlen(new_var.get()) == new_var_size);
+
+ {
+ auto& pm = pointer_map();
+ std::lock_guard lock(pm.env_mutex());
+ putenv(pm.register_pointer(std::move(new_var)));
+ }
+}
+
+void EnvReal::unset (std::string_view name) noexcept {
+ auto& pm = pointer_map();
+ std::lock_guard lock(pm.env_mutex());
+
+ duck::invoke_with_zstr(
+ name,
+ m_name_buff.get(),
+ m_name_buff_len,
+ [&pm](const char* name, std::size_t) -> void {
+ const char* ptr = duck::raw_fetch_env(name);
+ pm.delete_if_known(ptr);
+ unsetenv(name);
+ }
+ );
+}
+
+bool EnvReal::is_set (std::string_view name) const noexcept {
+}
+
+auto EnvReal::size() const noexcept -> size_type {
+ auto& pm = pointer_map();
+ std::lock_guard lock(pm.env_mutex());
+
+ assert(environ);
+ size_type z = 0;
+ while (environ[z]) {
+ ++z;
+ }
+ return z;
+}
+
+void EnvReal::clear() noexcept {
+ auto& pm = pointer_map();
+ std::lock_guard lock(pm.env_mutex());
+ const int r = clearenv();
+ if (not r) {
+ pm.clear();
+ }
+ else {
+ safe_print_error("clearenv() failed with return code " + std::to_string(r));
+ }
+}
+
+const char* EnvReal::raw_fetch_env (std::string_view name) const noexcept {
+ auto& pm = pointer_map();
+ std::lock_guard lock(pm.env_mutex());
+
+ return duck::invoke_with_zstr(
+ name,
+ m_name_buff.get(),
+ m_name_buff_len,
+ [](const char* name, std::size_t) {
+ return duck::raw_fetch_env(name);
+ }
+ );
+}
+
+} //namespace duck
diff --git a/src/env_real.hpp b/src/env_real.hpp
new file mode 100644
index 0000000..ab90e79
--- /dev/null
+++ b/src/env_real.hpp
@@ -0,0 +1,44 @@
+/* Copyright 2020, Michele Santullo
+ * This file is part of user-gcc.
+ *
+ * User-gcc is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * User-gcc is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with user-gcc. If not, see .
+ */
+
+#pragma once
+
+#include "env_base.hpp"
+#include
+
+namespace duck {
+ class EnvReal : public EnvBase {
+ public:
+ EnvReal();
+ virtual ~EnvReal() noexcept = default;
+
+ virtual std::string_view operator[] (std::string_view name) const noexcept override;
+ virtual std::optional get (std::string_view name) const noexcept override;
+ virtual std::string_view get (std::string_view name, std::string_view def) const noexcept override;
+ virtual void set (std::string_view name, std::string_view value) override;
+ virtual void unset (std::string_view name) noexcept override;
+ virtual bool is_set (std::string_view name) const noexcept override;
+ virtual size_type size() const noexcept override;
+ virtual void clear() noexcept override;
+
+ private:
+ const char* raw_fetch_env (std::string_view name) const noexcept;
+
+ std::unique_ptr m_name_buff;
+ std::size_t m_name_buff_len;
+ };
+} //namespace duck
diff --git a/src/meson.build b/src/meson.build
index 9a01898..20eb946 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -19,6 +19,8 @@ executable(meson.project_name(),
'to_var_map.cpp',
'load_file.cpp',
'var_map.cpp',
+ 'env_base.cpp',
+ 'env_real.cpp',
install: true,
dependencies: [fslib_dep],
include_directories: [cxxopts_incl],