split ini

This commit is contained in:
isanae
2020-12-03 17:45:32 -05:00
parent f54b879698
commit aa8efaa515
8 changed files with 487 additions and 446 deletions
+1
View File
@@ -1,5 +1,6 @@
#include "pch.h"
#include "commands.h"
#include "../core/ini.h"
#include "../tasks/tasks.h"
namespace mob
+1
View File
@@ -3,6 +3,7 @@
#include "../utility.h"
#include "../net.h"
#include "../core/conf.h"
#include "../core/ini.h"
#include "../tasks/tasks.h"
#include "../tools/tools.h"
#include "../utility/threading.h"
+134 -444
View File
File diff suppressed because it is too large Load Diff
+9 -2
View File
@@ -3,8 +3,6 @@
namespace mob
{
std::string default_ini_filename();
std::vector<fs::path> find_inis(
bool auto_detect, const std::vector<std::string>& from_cl,
bool verbose);
@@ -17,6 +15,8 @@ void log_options();
void dump_available_options();
fs::path find_in_path(std::string_view exe);
fs::path find_root(bool verbose=false);
fs::path find_in_root(const fs::path& file);
fs::path make_temp_file();
@@ -29,6 +29,13 @@ namespace details
void set_string(
std::string_view section, std::string_view key,
std::string_view value);
void add_string(
std::string_view section, std::string_view key, std::string_view value);
void set_string_for_task(
std::string_view task_name, std::string_view section,
std::string_view key, std::string_view value);
}
+318
View File
@@ -0,0 +1,318 @@
#include "pch.h"
#include "ini.h"
#include "context.h"
#include "env.h"
#include "conf.h"
#include "../tasks/tasks.h"
#include "../utility/string.h"
namespace mob
{
std::string default_ini_filename()
{
return "mob.ini";
}
parsed_option parse_option(const std::string& s)
{
// task:section/key=value
// task: is optional
std::regex re(R"((?:(.+)\:)?(.+)/(.*)=(.*))");
std::smatch m;
if (!std::regex_match(s, m, re))
{
gcx().bail_out(context::conf,
"bad option {}, must be [task:]section/key=value", s);
}
std::string task = trim_copy(m[1].str());
std::string section = trim_copy(m[2].str());
std::string key = trim_copy(m[3].str());
std::string value = trim_copy(m[4].str());
return {task, section, key, value};
}
template <class... Args>
void ini_error(
const fs::path& ini, std::size_t line, std::string_view f, Args&&... args)
{
gcx().bail_out(context::conf,
"{}:{}: {}",
path_to_utf8(ini), (line + 1),
fmt::format(f, std::forward<Args>(args)...));
}
std::vector<std::string> read_ini(const fs::path& ini)
{
std::ifstream in(ini);
std::vector<std::string> lines;
for (;;)
{
std::string line;
std::getline(in, line);
trim(line);
if (!in)
break;
lines.push_back(std::move(line));
}
if (in.bad())
gcx().bail_out(context::conf, "failed to read ini {}", ini);
return lines;
}
void parse_section(
const fs::path& ini, std::size_t& i,
const std::vector<std::string>& lines,
std::string_view task, std::string_view section, bool add)
{
++i;
for (;;)
{
if (i >= lines.size() || lines[i][0] == '[')
break;
const auto& line = lines[i];
if (line.empty() || line[0] == '#' || line[0] == ';')
{
++i;
continue;
}
const auto sep = line.find("=");
if (sep == std::string::npos)
ini_error(ini, i, "bad line '{}'", line);
const std::string k = trim_copy(line.substr(0, sep));
const std::string v = trim_copy(line.substr(sep + 1));
if (k.empty())
ini_error(ini, i, "bad line '{}'", line);
if (section == "aliases")
{
add_alias(k, split_quoted(v, " "));
}
else if (task.empty())
{
if (add)
details::add_string(section, k, v);
else
details::set_string(section, k, v);
}
else
{
if (task == "_override")
{
details::set_string_for_task("_override", section, k, v);
}
else
{
const auto& tasks = find_tasks(task);
if (tasks.empty())
ini_error(ini, i, "no task matching '{}' found", task);
for (auto& t : tasks)
details::set_string_for_task(t->name(), section, k, v);
}
}
++i;
}
}
void parse_ini(const fs::path& ini, bool add)
{
gcx().debug(context::conf, "using ini at {}", ini);
const auto lines = read_ini(ini);
std::size_t i = 0;
for (;;)
{
if (i >= lines.size())
break;
const auto& line = lines[i];
if (line.empty() || line[0] == '#' || line[0] == ';')
{
++i;
continue;
}
if (line.starts_with("[") && line.ends_with("]"))
{
const std::string s = line.substr(1, line.size() - 2);
std::string task, section;
const auto col = s.find(":");
if (col == std::string::npos)
{
section = s;
}
else
{
task = s.substr(0, col);
section = s.substr(col + 1);
}
parse_section(ini, i, lines, task, section, add);
}
else
{
ini_error(ini, i, "bad line '{}'", line);
}
}
}
std::vector<fs::path> find_inis(
bool auto_detect, const std::vector<std::string>& from_cl, bool verbose)
{
// the string is just for verbose
std::vector<std::pair<std::string, fs::path>> v;
// adds a path to the vector; if the path already exists, moves it to
// the last element
auto add_or_move_up = [&](std::string where, fs::path p)
{
for (auto itor=v.begin(); itor!=v.end(); ++itor)
{
if (fs::equivalent(p, itor->second))
{
auto pair = std::move(*itor);
v.erase(itor);
v.push_back({where + ", was " + pair.first, p});
return;
}
}
v.push_back({where, p});
};
fs::path master;
// auto detect from exe directory
if (auto_detect)
{
if (verbose)
{
const auto r = find_root(verbose);
u8cout << "root is " << path_to_utf8(r) << "\n";
}
master = find_in_root(default_ini_filename());
if (verbose)
u8cout << "found master " << path_to_utf8(master) << "\n";
v.push_back({"master", master});
}
// MOBINI environment variable
if (auto e=this_env::get_opt("MOBINI"))
{
if (verbose)
u8cout << "found env MOBINI: '" << *e << "'\n";
for (auto&& i : split(*e, ";"))
{
if (verbose)
u8cout << "checking '" << i << "'\n";
auto p = fs::path(i);
if (!fs::exists(p))
{
u8cerr << "ini from env MOBINI " << i << " not found\n";
throw bailed();
}
p = fs::canonical(p);
if (verbose)
u8cout << "ini from env: " << path_to_utf8(p) << "\n";
add_or_move_up("env", p);
}
}
// auto detect from the current directory
if (auto_detect)
{
MOB_ASSERT(!master.empty());
auto cwd = fs::current_path();
while (!cwd.empty()) {
const auto in_cwd = cwd / default_ini_filename();
if (fs::exists(in_cwd) && !fs::equivalent(in_cwd, master))
{
if (verbose)
u8cout << "also found in cwd " << path_to_utf8(in_cwd) << "\n";
v.push_back({ "cwd", fs::canonical(in_cwd) });
break;
}
const auto parent = cwd.parent_path();
if (cwd == parent)
break;
cwd = parent;
}
}
// command line
for (auto&& i : from_cl)
{
auto p = fs::path(i);
if (!fs::exists(p))
{
u8cerr << "ini " << i << " not found\n";
throw bailed();
}
p = fs::canonical(p);
if (verbose)
u8cout << "ini from command line: " << path_to_utf8(p) << "\n";
add_or_move_up("cl", p);
}
if (verbose)
{
u8cout << "\nhigher number overrides lower\n";
for (std::size_t i=0; i<v.size(); ++i)
{
u8cout
<< " " << (i + 1) << ") "
<< path_to_utf8(v[i].second) << " (" << v[i].first << ")\n";
}
}
return map(v, [&](auto&& p){ return p.second; });
}
} // namespace
+16
View File
@@ -0,0 +1,16 @@
#pragma once
namespace mob
{
std::string default_ini_filename();
void parse_ini(const fs::path& ini, bool add);
struct parsed_option
{
std::string task, section, key, value;
};
parsed_option parse_option(const std::string& s);
} // namespace
+2
View File
@@ -69,6 +69,7 @@
<ClCompile Include="..\src\core\conf.cpp" />
<ClCompile Include="..\src\core\context.cpp" />
<ClCompile Include="..\src\core\env.cpp" />
<ClCompile Include="..\src\core\ini.cpp" />
<ClCompile Include="..\src\core\op.cpp" />
<ClCompile Include="..\src\core\pipe.cpp" />
<ClCompile Include="..\src\core\process.cpp" />
@@ -125,6 +126,7 @@
<ClInclude Include="..\src\core\conf.h" />
<ClInclude Include="..\src\core\context.h" />
<ClInclude Include="..\src\core\env.h" />
<ClInclude Include="..\src\core\ini.h" />
<ClInclude Include="..\src\core\op.h" />
<ClInclude Include="..\src\core\pipe.h" />
<ClInclude Include="..\src\core\process.h" />
+6
View File
@@ -195,6 +195,9 @@
<ClCompile Include="..\src\core\pipe.cpp">
<Filter>src\core</Filter>
</ClCompile>
<ClCompile Include="..\src\core\ini.cpp">
<Filter>src\core</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\src\pch.h">
@@ -257,5 +260,8 @@
<ClInclude Include="..\src\core\pipe.h">
<Filter>src\core</Filter>
</ClInclude>
<ClInclude Include="..\src\core\ini.h">
<Filter>src\core</Filter>
</ClInclude>
</ItemGroup>
</Project>