First import

Emulates gcc-config -l so far.
This commit is contained in:
King_DuckZ 2020-04-09 02:17:32 +02:00
commit f58e278637
28 changed files with 1140 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
compile_commands.json
tags
build/

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "subprojects/cxxopts"]
path = subprojects/cxxopts
url = https://github.com/jarro2783/cxxopts.git

14
meson.build Normal file
View file

@ -0,0 +1,14 @@
project('user-gcc', 'cpp',
version: '0.1.0',
meson_version: '>=0.50.0',
default_options: ['buildtype=debug', 'cpp_std=gnu++17']
)
cxxopts_incl = include_directories('subprojects/cxxopts/include', is_system: true)
cpp = meson.get_compiler('cpp')
conf = configuration_data()
conf.set('PROJECT_NAME', meson.project_name())
conf.set('PROJECT_VERSION', meson.project_version())
subdir('src')

83
src/chost.cpp Normal file
View file

@ -0,0 +1,83 @@
#include "chost.hpp"
#include "env.hpp"
#include "run_cmd.hpp"
#include "gentoofunctions.hpp"
#include "replace_bash_vars.hpp"
#include <stdexcept>
#include <cassert>
namespace duck {
namespace {
std::string try_real_hard_to_find_CHOST() {
assert(false);
throw std::runtime_error("try_real_hard_to_find_CHOST() not implemented");
//#
//# First we read make.conf
//#
//local varname=${1:-CHOST}
//local conf=${EROOT}/etc/portage/make.conf
//if [[ ! -e ${conf} && -e ${EROOT}/etc/make.conf ]] ; then
// conf=${EROOT}/etc/make.conf
//fi
//local ret=$(source "${conf}" 2>/dev/null ; echo ${!varname})
//if [[ -z ${ret} ]] ; then
// # newer portage supports spaces between the var and =
// # CHOST = "this-is-retarded"
// ret=$(eval $(
// ${SED} -n \
// -e 's:[[:space:]]::g' \
// -e "/^${varname}=/p" \
// "${conf}"
// ) ; echo ${!varname}
// )
//fi
//if [[ -n ${ret} ]] ; then
// echo ${ret}
// return 0
//fi
//#
//# Then we try /etc/env.d/gcc/config-${CTARGET}
//#
//if [[ -s ${EROOT}/etc/env.d/gcc/config-${CTARGET} ]] ; then
// ret=$(split_gcc_ver $(show_var CURRENT "${EROOT}"/etc/env.d/gcc/config-${CTARGET}))
// echo ${ret% *}
//fi
}
} //unnamed namespace
std::string chost() {
using std::string;
{
// If it's set in the env, trust the setting. If it's wrong,
// then that's the caller's problem.
auto ret = env("CHOST");
if (ret and not ret->empty())
return string(*ret);
}
const int python_ret = run_cmd_retcode("python -V &>/dev/null");
std::string chost_str;
if (not python_ret) {
chost_str = run_cmd("portageq", "envvar", "CHOST").out;
}
else {
ewarn("Python seems to be broken, attempting to locate CHOST ourselves ...");
chost_str = try_real_hard_to_find_CHOST();
}
if (chost_str.empty()) {
eerror(replace_bash_vars("${argv0}: Could not get portage CHOST!"));
eerror(replace_bash_vars("${argv0}: You should verify that CHOST is set in one of these places:"));
eerror(replace_bash_vars("${argv0}: - ${EROOT}/etc/portage/make.conf"));
eerror(replace_bash_vars("${argv0}: - active environment"));
throw std::runtime_error(replace_bash_vars("${argv0}: Could not get portage CHOST!"));
}
return chost_str;
}
} //namespace duck

7
src/chost.hpp Normal file
View file

@ -0,0 +1,7 @@
#pragma once
#include <string>
namespace duck {
std::string chost();
} //namespace duck

4
src/config.h.in Normal file
View file

@ -0,0 +1,4 @@
#pragma once
#define PROJECT_NAME "@PROJECT_NAME@"
#define PROJECT_VERSION "@PROJECT_VERSION@"

76
src/env.cpp Normal file
View file

@ -0,0 +1,76 @@
#include "env.hpp"
#include <unistd.h>
#include <cassert>
#include <stdexcept>
#if defined(__GNU_LIBRARY__) && ((__GLIBC__ == 2 && __GLIBC_MINOR__ >= 17) || __GLIBC__ > 2)
# define HasSecureGetenv
#endif
namespace duck {
namespace {
const char* raw_fetch_env (const char* name) {
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;
}
} //unnamed namespace
std::string_view env (const char* name, std::string_view def) noexcept {
const auto ret = raw_fetch_env(name);
return (ret ? std::string_view(ret) : def);
}
std::optional<std::string_view> env (const char* name) noexcept {
using optional = std::optional<std::string_view>;
const auto ret = raw_fetch_env(name);
return (ret ? optional{ret} : optional{});
}
std::string_view env_throw (const char* name) {
const auto ret = raw_fetch_env(name);
if (ret) {
return {ret};
}
else {
if (name) {
throw std::runtime_error(
std::string("Environment variable \"") + name + "\" not set"
);
}
else {
throw std::runtime_error("Environment variable name is null");
}
}
}
bool is_env_set (const char* name) noexcept {
const auto ret = raw_fetch_env(name);
return not not ret;
}
std::string_view env (const std::string& name, std::string_view def) noexcept {
return env(name.c_str(), def);
}
std::optional<std::string_view> env (const std::string& name) noexcept {
return env(name.c_str());
}
std::string_view env_throw (const std::string& name) {
return env_throw(name.c_str());
}
bool is_env_set (const std::string& name) noexcept {
return is_env_set(name.c_str());
}
} //namespace duck

19
src/env.hpp Normal file
View file

@ -0,0 +1,19 @@
#pragma once
#include <optional>
#include <string_view>
#include <string>
namespace duck {
std::string_view env (const char* name, std::string_view def) noexcept;
std::optional<std::string_view> env (const char* name) noexcept;
std::string_view env_throw (const char* name);
bool is_env_set (const char* name) noexcept;
std::string_view env (const std::string& name, std::string_view def) noexcept;
std::optional<std::string_view> env (const std::string& name) noexcept;
std::string_view env_throw (const std::string& name);
bool is_env_set (const std::string& name) noexcept;
} //namespace duck

153
src/gentoofunctions.cpp Normal file
View file

@ -0,0 +1,153 @@
#include "gentoofunctions.hpp"
#include "env.hpp"
#include "string_view_cat.hpp"
#include "config.h"
#include "run_cmd.hpp"
#include <iostream>
#if __cplusplus == 201703L
# include <experimental/array>
#else
# error "If make_array() has been moved out of experimental please fix"
#endif
#include <algorithm>
namespace duck {
namespace {
std::string_view g_last_e_cmd;
enum YesNoIdk {
No, Yes, Idk
};
std::string lowercased (std::string_view in) {
std::string retval('\0', in.size());
std::transform(
in.begin(),
in.end(),
retval.begin(),
[](char c)->char{return std::tolower(c);}
);
return retval;
}
template <typename I, typename T>
bool has_item (I begin, const I& end, const T& val) {
auto it = std::find(begin, end, val);
return it != end;
}
YesNoIdk yesno_impl (std::string_view val) {
using std::experimental::make_array;
using std::string_view;
auto lcval = lowercased(val);
auto yesses = make_array<string_view>("yes", "true", "on", "1");
auto noes = make_array<string_view>("no", "false", "off", "0");
if (has_item(yesses.begin(), yesses.end(), lcval)) {
return Yes;
}
else if (has_item(noes.begin(), noes.end(), lcval)) {
return No;
}
else {
return Idk;
}
}
void generic_eprint (
std::ostream& stream,
const char* quiet_var,
const char* msg_prefix_var,
std::string_view log_pri,
std::string_view log_tag,
std::string_view new_e_cmd,
std::string_view msg,
const char* newline
) {
if (yesno(env(quiet_var, ""))) {
return;
}
else {
if (not yesno(env("RC_ENDCOL", "")) and g_last_e_cmd == "ebegin") {
stream << '\n';
}
stream << ' ' << env(msg_prefix_var, "") << '*'
<< env("NORMAL", "") << ' ' << env("RC_INDENTATION", "")
<< msg << newline;
}
esyslog(log_pri, log_tag, msg);
g_last_e_cmd = new_e_cmd;
}
} //unnamed namespace
void vewarn (std::string_view msg) {
if (yesno(env("EINFO_VERBOSE", ""), true))
ewarn(msg);
}
void ewarn (std::string_view msg) {
generic_eprint(
std::clog,
"EINFO_QUIET",
"WARN",
"daemon.warning",
PROJECT_NAME,
"ewarn",
msg,
"\n"
);
}
void esyslog(std::string_view pri, std::string_view tag, std::string_view msg) {
if (not env("EINFO_LOG", "").empty() and not run_cmd_retcode("command -v logger > /dev/null 2>&1")) {
if (msg.empty())
return;
run_cmd("logger", "-p", pri, "-t", tag, "--", msg);
}
}
bool yesno (const std::string& var, bool force_quiet) {
{
const auto ret = yesno_impl(var);
switch (ret) {
case Yes: return true;
case No: return false;
default: break;
}
}
{
auto val = env(var, "");
if (val.empty())
return false;
const auto ret = yesno_impl(val);
switch (ret) {
case Yes: return true;
case No: return false;
default:
vewarn(std::string("\"") + var + "\" (\"" + val + "\") is not set properly");
return false;
}
}
}
bool yesno (std::string_view var, bool force_quiet) {
return yesno(to_string(var), force_quiet);
}
void eerror (std::string_view msg) {
generic_eprint(
std::clog,
"EERROR_QUIET",
"BAD",
"daemon.err",
"rc-scripts",
"eerrorn",
msg,
"\n"
);
}
} //namespace duck

13
src/gentoofunctions.hpp Normal file
View file

@ -0,0 +1,13 @@
#pragma once
#include <string_view>
#include <string>
namespace duck {
void vewarn (std::string_view msg);
void ewarn (std::string_view msg);
bool yesno (const std::string& var, bool force_quiet=false);
bool yesno (std::string_view var, bool force_quiet=false);
void esyslog(std::string_view pri, std::string_view tag, std::string_view msg);
void eerror (std::string_view msg);
} //namespace duck

164
src/list_profiles.cpp Normal file
View file

@ -0,0 +1,164 @@
#include "list_profiles.hpp"
#include "syspaths.hpp"
#include "config.h"
#include "chost.hpp"
#include "to_var_map.hpp"
#include "load_file.hpp"
#include "string_view_cat.hpp"
#include "gentoofunctions.hpp"
#include <iostream>
#include <filesystem>
#include <vector>
#include <string>
#include <algorithm>
#include <regex>
namespace duck {
namespace fs = std::filesystem;
namespace {
[[gnu::pure]]
bool starts_with (std::string_view s, std::string_view prefix) {
return (s.substr(0, std::min(prefix.size(), s.size())) == prefix);
}
std::vector<int> decompose_version (const std::string& ver) {
std::vector<int> ret;
int curr = 0;
bool push_last = false;
for (char c : ver) {
if (c >= '0' and c <= '9') {
curr = (curr * 10) + (c - '0');
push_last = true;
}
else if (push_last) {
ret.push_back(curr);
curr = 0;
push_last = false;
}
}
if (push_last)
ret.push_back(curr);
return ret;
}
[[gnu::pure]]
bool version_comp (const std::string& a, const std::string& b) {
std::vector<int> va(decompose_version(a));
std::vector<int> vb(decompose_version(b));
const std::size_t sz = std::max(va.size(), vb.size());
va.resize(sz, 0);
vb.resize(sz, 0);
for (std::size_t z = 0; z < sz; ++z) {
if (va[z] < vb[z])
return true;
else if (va[z] > vb[z])
return false;
}
return false;
}
std::string current_compiler (const fs::path& config_path, const std::string& ctarget) {
if (fs::is_regular_file(config_path)) {
auto text = load_file(config_path);
auto var_map = to_var_map(text);
auto found_it = var_map.find("CURRENT");
if (var_map.end() == found_it)
return {};
else
return to_string(found_it->second);
}
else {
return {};
}
}
} //unnamed namespace
std::vector<fs::path> version_sorted_paths (const fs::path path) {
std::vector<fs::path> ret;
for (const fs::directory_entry& curr : fs::directory_iterator(path)) {
std::string filename = curr.path().filename();
if (not starts_with(filename, "config") and curr.is_regular_file())
ret.push_back(curr);
}
std::regex version_reg(
R"(-(\d+(?:\.\d)*(?:r\d+)?))",
std::regex_constants::ECMAScript | std::regex_constants::optimize
);
std::sort(
ret.begin(),
ret.end(),
[&version_reg](const fs::path& a, const fs::path& b) {
using std::regex_match;
std::smatch ma, mb;
std::string sa = a.filename();
std::string sb = b.filename();
return version_comp(
(regex_search(sa, ma, version_reg) ? ma[1] : std::string("0")),
(regex_search(sb, mb, version_reg) ? mb[1] : std::string("0"))
);
}
);
return ret;
}
void list_profiles(const SysPaths& sp) {
using std::string;
if (sp.root() != "/") {
//echo "Using gcc-config info in ${ROOT}"
std::cout << "Using " << PROJECT_NAME << " info in " << sp.root() << '\n';
}
std::string chost = duck::chost();
const std::string& ctarget = chost;
std::string_view filter;
auto config_path = sp.gcc_env_d() / ("config-" + ctarget);
auto s = fs::status(config_path);
if (not fs::is_regular_file(s)) {
filter = ctarget;
}
const std::string current = current_compiler(config_path, ctarget);
std::string_view current_native(current);
auto version_paths = version_sorted_paths(sp.gcc_env_d());
std::string text;
std::size_t z = 0;
std::string target;
for (const auto& ver_path : version_paths) {
text = load_file(ver_path);
auto var_map = to_var_map(text);
auto curr_ctarget = var_map["CTARGET"];
std::string filename = ver_path.filename();
if (curr_ctarget.empty()) {
if (starts_with(filename, chost + "-"))
curr_ctarget = chost;
else
ewarn("broken config file: " + std::string(ver_path));
}
++z;
if (not filter.empty() and filter != curr_ctarget)
continue;
if (target != curr_ctarget) {
if (z > 1)
std::cout << '\n';
target = to_string(curr_ctarget);
curr_ctarget = "";
}
std::cout << " [" << z << "] " << filename;
auto colours = sp.colours();
if (filename == current_native) {
std::cout << ' ' << colours["GOOD"] << '*' << colours["NORMAL"];
}
else if (fs::is_regular_file(sp.gcc_env_d() / ("config-" + target)) and filename == current) {
std::cout << ' ' << colours["HILITE"] << '*' << colours["NORMAL"];
}
std::cout << '\n';
}
}
} //namespace duck

7
src/list_profiles.hpp Normal file
View file

@ -0,0 +1,7 @@
#pragma once
namespace duck {
class SysPaths;
void list_profiles(const SysPaths& syspaths);
} //namespace duck

14
src/load_file.cpp Normal file
View file

@ -0,0 +1,14 @@
#include "load_file.hpp"
#include <fstream>
namespace duck {
std::string load_file (const std::string& path) {
std::ifstream ifs(path);
ifs >> std::noskipws;
return std::string(
std::istreambuf_iterator<char>(ifs),
std::istreambuf_iterator<char>()
);
}
} //namespace duck

7
src/load_file.hpp Normal file
View file

@ -0,0 +1,7 @@
#pragma once
#include <string>
namespace duck {
std::string load_file (const std::string& path);
} //namespace duck

50
src/main.cpp Normal file
View file

@ -0,0 +1,50 @@
#include <iostream>
#include <cxxopts.hpp>
#include <string>
#include "config.h"
#include "list_profiles.hpp"
#include "syspaths.hpp"
namespace {
const char g_simple_colours[] =
"GOOD=\033[32;01m\n"
"WARN=\033[33;01m\n"
"BAD=\033[31;01m\n"
"HILITE=\033[36;01m\n"
"BRACKET=\033[34;01m\n"
"NORMAL=\033[0m\n"
;
} //unnamed namespace
int main(int argc, char* argv[]) {
using std::string;
cxxopts::Options options(PROJECT_NAME, "Change the current compiler profile, or give info about profiles");
options.add_options()
("h,help", "Show this help and quit")
("version", "Print version info")
("l,list-profiles", "Print a list of available profiles") //, cxxopts::value<string>()->default_value(""))
;
const int orig_argc = argc;
auto command = options.parse(argc, argv);
if (command.count("help") or 1 == orig_argc) {
std::cout << options.help() << std::endl;
return 0;
}
if (command.count("version")) {
std::cout << PROJECT_NAME << " v" << PROJECT_VERSION << std::endl;
return 0;
}
duck::SysPaths syspaths;
syspaths.set_colours(g_simple_colours);
#if !defined(NDEBUG)
std::cout << syspaths << '\n';
#endif
if (command.count("list-profiles")) {
duck::list_profiles(syspaths);
}
return 0;
}

26
src/meson.build Normal file
View file

@ -0,0 +1,26 @@
fslib_dep = cpp.find_library('stdc++fs', required: false)
cpp.has_header('pstream.h', required: true)
project_config_file = configure_file(
input: 'config.h.in',
output: 'config.h',
configuration: conf
)
executable(meson.project_name(),
'main.cpp',
'env.cpp',
project_config_file,
'list_profiles.cpp',
'syspaths.cpp',
'chost.cpp',
'run_cmd.cpp',
'gentoofunctions.cpp',
'replace_bash_vars.cpp',
'to_var_map.cpp',
'load_file.cpp',
'var_map.cpp',
install: true,
dependencies: [fslib_dep],
include_directories: [cxxopts_incl],
)

94
src/replace_bash_vars.cpp Normal file
View file

@ -0,0 +1,94 @@
#include "replace_bash_vars.hpp"
#include "config.h"
#include "env.hpp"
#include "string_view_cat.hpp"
#include <cassert>
#include <cctype>
namespace duck {
namespace {
struct VarInfo {
std::string name;
std::size_t orig_len{};
};
//std::string replaced_all (std::string text, std::string_view search, std::string_view repl) {
// std::size_t pos = 0;
// while ((pos = text.find(search, pos)) != std::string::npos) {
// text.replace(pos, search.size(), repl);
// pos += repl.size();
// }
// return text;
//}
VarInfo extract_var_name (std::string_view text) {
assert(not text.empty());
assert(text.front() == '$');
if (text.size() == 1) {
return {};
}
else if (text.size() == 2) {
if (std::isalnum(text[1]))
return {to_string(text.substr(1, 1)), 2};
return {};
}
else {
assert(text.size() >= 3);
if (text[1] == '{') {
const auto end = text.find('}', 2);
if (text.npos == end) {
return {};
}
else {
std::string_view token(text.substr(2, end - 2));
std::string retval(token.empty() || token.front() != '!' ? to_string(token) : to_string(token.substr(1)));
if (not token.empty() and token.front() == '!') {
return {to_string(env(retval, "")), end + 1};
}
else {
return {retval, end + 1};
}
}
}
else {
std::size_t z;
for (z = 1; z < text.size() && std::isalnum(text[z]); ++z) {
}
return {to_string(text.substr(1, z - 1)), z + 1};
}
}
}
} //unnamed namespace
std::string replace_bash_vars (std::string_view text) {
std::size_t pos = 0;
std::size_t prev_start = 0;
std::string rtext;
rtext.reserve(text.size());
while ((pos = text.find('$', pos)) != std::string::npos) {
if (0 == pos or rtext[pos-1] != '\\') {
auto var = extract_var_name(text.substr(pos));
if (not var.orig_len) {
++pos;
continue;
}
assert(prev_start <= pos);
rtext.append(text.begin() + prev_start, text.begin() + pos);
if (var.name == "argv0")
rtext += PROJECT_NAME;
else
rtext += env(var.name, "");
pos += var.orig_len;
prev_start = pos;
}
else {
++pos;
}
}
rtext.append(text.begin() + prev_start, text.end());
return rtext;
}
} //namespace duck

View file

@ -0,0 +1,8 @@
#pragma once
#include <string_view>
#include <string>
namespace duck {
std::string replace_bash_vars (std::string_view text);
} //namespace duck

74
src/run_cmd.cpp Normal file
View file

@ -0,0 +1,74 @@
#include "run_cmd.hpp"
#include <cstdio>
#include <cstdlib>
#if !defined(NDEBUG)
# include <iostream>
# include <algorithm>
#endif
#include <stdexcept>
#include <memory>
#include <pstream.h>
#include <array>
#include <cassert>
namespace duck::detail {
namespace {
void trim_eol (std::string& val) {
const auto pos = val.find_last_not_of('\n');
if (val.npos != pos)
val.resize(pos + 1);
}
} //unnamed namespace
RunCmdOutput run_cmd(const std::string& cmd, const std::vector<std::string>& argv) {
assert(not argv.empty());
assert(argv.front() == cmd);
#if !defined(NDEBUG)
std::cout << "Running command: " << cmd;
std::for_each(argv.begin(), argv.end(), [](const auto& s){std::cout << ' ' << s;});
std::cout << '\n';
#endif
const redi::pstream::pmode mode = redi::pstreams::pstdout | redi::pstreams::pstderr;
redi::ipstream ips(cmd, argv, mode);
std::streamsize n;
RunCmdOutput ret;
std::array<char, 64> buf;
std::array<bool, 2> finished {false, false};
while (not finished[0] or not finished[1]) {
if (not finished[0]) {
while ((n = ips.err().readsome(buf.data(), buf.size())) > 0)
ret.err.append(buf.begin(), buf.begin() + n);
if (ips.eof()) {
finished[0] = true;
if (not finished[1])
ips.clear();
}
}
if (!finished[1]) {
while ((n = ips.out().readsome(buf.data(), buf.size())) > 0)
ret.out.append(buf.begin(), buf.begin() + n);
if (ips.eof()) {
finished[1] = true;
if (!finished[0])
ips.clear();
}
}
}
trim_eol(ret.out);
trim_eol(ret.err);
return ret;
}
} //namespace detail::duck
namespace duck {
int run_cmd_retcode (const char* command) {
assert(command);
return system(command);
}
} //namespace duck

31
src/run_cmd.hpp Normal file
View file

@ -0,0 +1,31 @@
#pragma once
#include "string_view_cat.hpp"
#include <string>
#include <string_view>
#include <utility>
#include <vector>
namespace duck {
struct RunCmdOutput {
std::string out;
std::string err;
};
namespace detail {
RunCmdOutput run_cmd(const std::string& cmd, const std::vector<std::string>& argv);
} //namespace detail
template <typename... Args>
inline RunCmdOutput run_cmd (std::string command, Args&&... args) {
using duck::to_string;
std::vector<std::string> argv {
command,
to_string(std::forward<Args>(args))...
};
return detail::run_cmd(command, argv);
}
int run_cmd_retcode (const char* command);
} //namespace duck

39
src/string_view_cat.hpp Normal file
View file

@ -0,0 +1,39 @@
#pragma once
#include <string>
#include <string_view>
namespace duck {
inline std::string to_string (std::string_view sv) {
return std::string((sv.empty() ? static_cast<const char*>("") : sv.data()), sv.size());
}
inline std::string to_string (const char* str) {
return std::string(str ? str : static_cast<const char*>(""));
}
inline std::string to_string (std::string str) {
return str;
}
inline std::string operator+ (std::string_view a, std::string_view b) {
std::string ret(to_string(a));
ret += b;
return ret;
}
inline std::string operator+ (std::string_view a, const std::string& b) {
std::string ret(to_string(a));
ret += b;
return ret;
}
inline std::string operator+ (std::string a, std::string_view b) {
a += b;
return a;
}
inline std::string operator+ (const char* a, std::string_view b) {
return to_string(a) + b;
}
} //namespace duck

68
src/syspaths.cpp Normal file
View file

@ -0,0 +1,68 @@
#include "syspaths.hpp"
#include "env.hpp"
#include "to_var_map.hpp"
#include <unistd.h>
namespace duck {
namespace {
std::string fix_eprefix(std::string_view p) {
if (p.size() >= 2 and p.front() == '@' and p.back() == '@')
return "";
else
return std::string(p);
}
std::string_view remove_front_slash(std::string_view text) {
const std::size_t pos = text.find_first_not_of('/');
return (text.npos == pos ? text : text.substr(pos));
}
} //unnamed namespace
SysPaths::SysPaths() :
m_env(to_var_map_ignore_ownership(environ)),
m_root(duck::env("ROOT", "/")),
m_eprefix(fix_eprefix(duck::env("EPREFIX", ""))),
m_eroot(m_root / remove_front_slash(std::string(m_eprefix))),
m_env_d(m_eroot / "etc/env.d"),
m_gcc_env_d(m_env_d / "gcc")
{
}
void SysPaths::set_colours (std::string_view colours) {
m_colours = to_var_map_ignore_ownership(colours);
}
auto SysPaths::root() const -> const path_type& {
return m_root;
}
auto SysPaths::eroot() const -> const path_type& {
return m_eroot;
}
auto SysPaths::eprefix() const -> const path_type& {
return m_eprefix;
}
auto SysPaths::env_d() const -> const path_type& {
return m_env_d;
}
auto SysPaths::gcc_env_d() const -> const path_type& {
return m_gcc_env_d;
}
#if !defined(NDEBUG)
std::ostream& operator<< (std::ostream& oss, const SysPaths& sp) {
oss << "SysPaths {\n" <<
"\tROOT: " << sp.root() << '\n' <<
"\tEPREFIX: " << sp.eprefix() << '\n' <<
"\tEROOT: " << sp.eroot() << '\n' <<
"\tENV_D: " << sp.env_d() << '\n' <<
"\tGCC_ENV_D: " << sp.gcc_env_d() << '\n' <<
'}';
return oss;
}
#endif
} //namespace duck

43
src/syspaths.hpp Normal file
View file

@ -0,0 +1,43 @@
#pragma once
#include "var_map.hpp"
#include <filesystem>
#include <string>
#if !defined(NDEBUG)
# include <iostream>
#endif
#include <unordered_map>
#include <string_view>
namespace duck {
class SysPaths {
public:
typedef std::filesystem::path path_type;
SysPaths();
~SysPaths() noexcept = default;
void set_colours (std::string_view colours);
VarMapView colours() const { return {&m_colours}; }
VarMapView env() const { return {&m_env}; }
const path_type& root() const;
const path_type& eroot() const;
const path_type& eprefix() const;
const path_type& env_d() const;
const path_type& gcc_env_d() const;
private:
VarMap m_env;
VarMap m_colours;
path_type m_root;
path_type m_eprefix;
path_type m_eroot;
path_type m_env_d;
path_type m_gcc_env_d;
};
#if !defined(NDEBUG)
std::ostream& operator<< (std::ostream&, const SysPaths& sp);
#endif
} //namespace duck

87
src/to_var_map.cpp Normal file
View file

@ -0,0 +1,87 @@
#include "to_var_map.hpp"
#include "string_view_cat.hpp"
#include <stdexcept>
#include <cassert>
#include <cctype>
#include <algorithm>
namespace duck {
namespace {
std::string_view trimmed (std::string_view text) {
using std::find_if_not;
auto isspace_func = [](char c) {return std::isspace(c);};
const auto new_start_it = find_if_not(text.cbegin(), text.cend(), isspace_func);
const std::size_t new_start = (new_start_it == text.cend() ? text.size() : static_cast<std::size_t>(new_start_it - text.cbegin()));
const auto new_end_it = find_if_not(text.crbegin(), text.crend(), isspace_func);
const std::size_t new_end = text.size() - (new_end_it == text.crend() ? std::size_t{} : static_cast<std::size_t>(new_end_it - text.crbegin()));
assert(new_end <= text.size());
assert(new_end >= new_start);
return text.substr(new_start, new_end - new_start);
}
std::pair<std::string_view, std::string_view> split_key_val(std::string_view text) {
const auto eq_pos = text.find('=');
if (eq_pos == text.npos) {
throw std::runtime_error("Unrecognised line \"" + text + "\": expected key=value format not found");
}
return std::make_pair(
text.substr(0, eq_pos),
text.substr(eq_pos + 1)
);
}
std::string_view unquoted (std::string_view text) {
std::string_view ret = trimmed(text);
if (ret.size() < 2)
return ret;
else if (ret.front() == '"' and ret.back() == '"')
return ret.substr(1, ret.size() - 2);
else
return ret;
}
void add_key_val_text_to_map (std::string_view text, VarMap& out) {
std::string_view line = trimmed(text);
if (line.empty() or line.front() == '#')
return;
auto new_item = split_key_val(line);
out[new_item.first] = unquoted(new_item.second);
}
} //unnamed namespace
VarMap to_var_map (std::string& text) {
return to_var_map_ignore_ownership(text);
}
VarMap to_var_map_ignore_ownership (std::string_view text) {
VarMap ret;
std::size_t pos = 0;
std::size_t start = 0;
while ((pos = text.find('\n', pos)) != text.npos) {
add_key_val_text_to_map(text.substr(start, pos - start), ret);
start = pos + 1;
++pos;
}
add_key_val_text_to_map(text.substr(start), ret);
return ret;
}
VarMap to_var_map_ignore_ownership (char** env) {
assert(env);
VarMap ret;
std::size_t z = 0;
while (env[z]) {
std::string_view line(env[z]);
add_key_val_text_to_map(line, ret);
++z;
}
return ret;
}
} //namespace duck

13
src/to_var_map.hpp Normal file
View file

@ -0,0 +1,13 @@
#pragma once
#include "var_map.hpp"
#include <string>
namespace duck {
//parameter is non-const so caller hopefully remembers to keep it alive
//(ie: they can't just pass a temporary in)
VarMap to_var_map (std::string& text);
VarMap to_var_map_ignore_ownership (std::string_view text);
VarMap to_var_map_ignore_ownership (char** env);
} //namespace duck

22
src/var_map.cpp Normal file
View file

@ -0,0 +1,22 @@
#include "var_map.hpp"
#include <cassert>
namespace duck {
VarMapView::VarMapView (const VarMap* vm) :
m_map(vm)
{
assert(m_map);
}
std::string_view VarMapView::operator[] (std::string_view key) const {
if (key.empty())
return "";
assert(m_map);
auto it_found = m_map->find(key);
if (m_map->end() != it_found)
return it_found->second;
else
return "";
}
} //namespace duck

17
src/var_map.hpp Normal file
View file

@ -0,0 +1,17 @@
#pragma once
#include <unordered_map>
#include <string_view>
namespace duck {
typedef std::unordered_map<std::string_view, std::string_view> VarMap;
class VarMapView {
public:
VarMapView (const VarMap* vm);
std::string_view operator[] (std::string_view key) const;
private:
const VarMap* m_map;
};
} //namespace duck

1
subprojects/cxxopts Submodule

@ -0,0 +1 @@
Subproject commit b0f67a06de3446aa97a4943ad0ad6086460b2b61