Add a still incomplete EnvReal class.
So far it allows to read and modify environment variables.
This commit is contained in:
parent
f7cdcd13a2
commit
4cc1aa491e
5 changed files with 382 additions and 0 deletions
24
src/env_base.cpp
Normal file
24
src/env_base.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "env_base.hpp"
|
||||||
|
|
||||||
|
namespace duck {
|
||||||
|
bool EnvBase::empty() const noexcept {
|
||||||
|
return 0 == this->size();
|
||||||
|
}
|
||||||
|
} //namespace duck
|
46
src/env_base.hpp
Normal file
46
src/env_base.hpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string_view>
|
||||||
|
#include <optional>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace duck {
|
||||||
|
class EnvBase {
|
||||||
|
public:
|
||||||
|
typedef std::string_view mapped_type;
|
||||||
|
typedef std::string_view key_type;
|
||||||
|
typedef std::pair<const key_type, mapped_type> 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<std::string_view> 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
|
266
src/env_real.cpp
Normal file
266
src/env_real.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
//required for putenv()
|
||||||
|
#define _XOPEN_SOURCE
|
||||||
|
|
||||||
|
#include "env_real.hpp"
|
||||||
|
#include "string_view_cat.hpp"
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <mutex>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <memory>
|
||||||
|
#include <cassert>
|
||||||
|
#include <utility>
|
||||||
|
#include <iostream>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#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<char[]> 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<const char*, pointer> m_ptrs;;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> struct DefaultValOrVoid {
|
||||||
|
static T value() noexcept(noexcept(T())) {
|
||||||
|
return T{};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template <> struct DefaultValOrVoid<void> {
|
||||||
|
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<std::mutex> lock(m_data_mutex);
|
||||||
|
m_ptrs.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
char* PointerMap::register_pointer (pointer&& ptr) {
|
||||||
|
std::lock_guard<std::mutex> 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<std::mutex> 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<std::mutex> 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 <typename F>
|
||||||
|
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<decltype(func(name_buff, buff_size))>::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<decltype(func(name_buff, buff_size))>::value();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} //unnamed namespace
|
||||||
|
|
||||||
|
EnvReal::EnvReal() :
|
||||||
|
m_name_buff(std::make_unique<char[]>(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<std::string_view> 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<char[]> new_var(std::make_unique<char[]>(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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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
|
44
src/env_real.hpp
Normal file
44
src/env_real.hpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "env_base.hpp"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
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<std::string_view> 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<char[]> m_name_buff;
|
||||||
|
std::size_t m_name_buff_len;
|
||||||
|
};
|
||||||
|
} //namespace duck
|
|
@ -19,6 +19,8 @@ executable(meson.project_name(),
|
||||||
'to_var_map.cpp',
|
'to_var_map.cpp',
|
||||||
'load_file.cpp',
|
'load_file.cpp',
|
||||||
'var_map.cpp',
|
'var_map.cpp',
|
||||||
|
'env_base.cpp',
|
||||||
|
'env_real.cpp',
|
||||||
install: true,
|
install: true,
|
||||||
dependencies: [fslib_dep],
|
dependencies: [fslib_dep],
|
||||||
include_directories: [cxxopts_incl],
|
include_directories: [cxxopts_incl],
|
||||||
|
|
Loading…
Reference in a new issue