mirror of
https://github.com/ModOrganizer2/mob.git
synced 2026-07-27 14:07:05 -07:00
look for root directory based on exe path, not cwd
simplified inis: look for mob.ini in root and cwd, add MOBINI and --ini added inis command options command now shows all the options after loading the inis
This commit is contained in:
+106
-46
@@ -14,6 +14,7 @@ std::string version()
|
||||
return "mob 3.0";
|
||||
}
|
||||
|
||||
|
||||
void help(const clipp::group& g, const std::string& more)
|
||||
{
|
||||
#pragma warning(suppress: 4548)
|
||||
@@ -48,6 +49,7 @@ command::command(flags f)
|
||||
clipp::group command::common_options_group()
|
||||
{
|
||||
auto& o = common;
|
||||
const auto master = master_ini_filename();
|
||||
|
||||
return
|
||||
(clipp::repeatable(clipp::option("-i", "--ini")
|
||||
@@ -78,7 +80,8 @@ clipp::group command::common_options_group()
|
||||
% "sets an option, such as 'versions/openssl=1.2'",
|
||||
|
||||
(clipp::option("--no-default-inis") >> o.no_default_inis)
|
||||
% "disables auto detection of ini files, only uses --ini";
|
||||
% "disables auto loading of ini files, only uses --ini; the first"
|
||||
"--ini must be the master ini file";
|
||||
}
|
||||
|
||||
void command::force_exit_code(int code)
|
||||
@@ -111,14 +114,8 @@ clipp::group command::group()
|
||||
return do_group();
|
||||
}
|
||||
|
||||
int command::run()
|
||||
void command::convert_cl_to_conf()
|
||||
{
|
||||
if (help_)
|
||||
{
|
||||
help(group(), do_doc());
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto& o = common;
|
||||
|
||||
if (o.file_log_level == -1)
|
||||
@@ -146,25 +143,53 @@ int command::run()
|
||||
|
||||
if (!o.prefix.empty())
|
||||
o.options.push_back("paths/prefix=" + o.prefix);
|
||||
}
|
||||
|
||||
do_pre_run();
|
||||
int command::gather_inis(bool verbose)
|
||||
{
|
||||
auto& o = common;
|
||||
|
||||
if (o.no_default_inis && o.inis.empty())
|
||||
{
|
||||
u8cerr
|
||||
<< "--no-default-inis requires at least one --ini for the "
|
||||
<< "master ini file\n";
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
inis_ = find_inis(!o.no_default_inis, o.inis, verbose);
|
||||
return 0;
|
||||
}
|
||||
catch(bailed&)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
int command::prepare_options(bool verbose)
|
||||
{
|
||||
convert_cl_to_conf();
|
||||
return gather_inis(verbose);
|
||||
}
|
||||
|
||||
int command::run()
|
||||
{
|
||||
if (help_)
|
||||
{
|
||||
help(group(), do_doc());
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (flags_ & requires_options)
|
||||
{
|
||||
if (o.no_default_inis && o.inis.empty())
|
||||
{
|
||||
u8cerr
|
||||
<< "--no-default-inis requires at least one --ini for the "
|
||||
<< "master ini file\n";
|
||||
const int r = prepare_options(false);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::vector<fs::path> inis;
|
||||
for (auto&& s : o.inis)
|
||||
inis.push_back(s);
|
||||
|
||||
init_options(inis, !o.no_default_inis, o.options);
|
||||
init_options(inis_, common.options);
|
||||
log_options();
|
||||
|
||||
if (!verify_options())
|
||||
@@ -179,6 +204,11 @@ int command::run()
|
||||
return r;
|
||||
}
|
||||
|
||||
const std::vector<fs::path>& command::inis() const
|
||||
{
|
||||
return inis_;
|
||||
}
|
||||
|
||||
|
||||
clipp::group version_command::do_group()
|
||||
{
|
||||
@@ -204,18 +234,19 @@ int help_command::do_run()
|
||||
#pragma warning(suppress: 4548)
|
||||
auto doc = (command::common_options_group(), (clipp::value("command")));
|
||||
|
||||
const auto mobini = master_ini_filename();
|
||||
const auto master = master_ini_filename();
|
||||
|
||||
help(doc,
|
||||
"Commands:\n"
|
||||
" help shows this message\n"
|
||||
" version shows the version\n"
|
||||
" list lists available tasks\n"
|
||||
" options lists available options and default values\n"
|
||||
" options lists all options and their values from the inis\n"
|
||||
" build builds tasks\n"
|
||||
" release creates a release or a devbuild\n"
|
||||
" git manages the git repos\n"
|
||||
" cmake runs cmake in a directory\n"
|
||||
" inis lists the INIs used by mob (debug)\n"
|
||||
"\n\n"
|
||||
"Invoking `mob -d some/prefix build` builds everything. Do \n"
|
||||
"`mob build <task name>...` to build specific tasks. See\n"
|
||||
@@ -223,25 +254,24 @@ int help_command::do_run()
|
||||
"\n"
|
||||
"INI files\n"
|
||||
"\n"
|
||||
"By default, mob will look for a master INI `" + mobini + "` in the \n"
|
||||
"current directory and up to three of its parents (so it can also be\n"
|
||||
"found from the build directory). Once mob found the master INI, it\n"
|
||||
"will look for any other .ini file in the same directory.\n"
|
||||
"\n"
|
||||
"These additional INI files will be loaded after the master, in\n"
|
||||
"lexicographical order, overriding anything the previous INI file\n"
|
||||
"may have set.\n"
|
||||
"\n"
|
||||
"Additional INI files may be specified with --ini. They will be\n"
|
||||
"loaded in order after the ones that were auto detected. If the same\n"
|
||||
"INI file is found in the directory and on the command line, its\n"
|
||||
"position in the load order will be moved to that of the command\n"
|
||||
"line.");
|
||||
"By default, mob will look for a master INI `" + master + "` in the \n"
|
||||
"root directory (typically where mob.exe resides). Once mob has\n"
|
||||
"found the master INI, it will look for the same filename in the\n"
|
||||
"current directory, if different from the root. If found, both will\n"
|
||||
"be loaded, but the one in the current directory will override the\n"
|
||||
"the other. Additional INIs can be specified with --ini, those will\n"
|
||||
"be loaded after the two mentioned above. Use --no-default-inis to\n"
|
||||
"only disable auto detection and only use --ini.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
options_command::options_command()
|
||||
: command(requires_options)
|
||||
{
|
||||
}
|
||||
|
||||
clipp::group options_command::do_group()
|
||||
{
|
||||
return clipp::group(
|
||||
@@ -260,10 +290,7 @@ int options_command::do_run()
|
||||
|
||||
std::string options_command::do_doc()
|
||||
{
|
||||
return
|
||||
"Lists all available options in the form of `section/key = default`.\n"
|
||||
"They can be changed in the INI file by setting `key = value` within\n"
|
||||
"`[section]` or with `-s section/key=value`.";
|
||||
return "Lists the final value of all options found by loading the INIs.";
|
||||
}
|
||||
|
||||
|
||||
@@ -304,16 +331,18 @@ clipp::group build_command::do_group()
|
||||
"projects";
|
||||
}
|
||||
|
||||
void build_command::do_pre_run()
|
||||
void build_command::convert_cl_to_conf()
|
||||
{
|
||||
command::convert_cl_to_conf();
|
||||
|
||||
if (redownload_ || clean_)
|
||||
common.options.push_back("options/redownload=true");
|
||||
common.options.push_back("global/redownload=true");
|
||||
|
||||
if (reextract_ || clean_)
|
||||
common.options.push_back("options/reextract=true");
|
||||
common.options.push_back("global/reextract=true");
|
||||
|
||||
if (rebuild_ || clean_)
|
||||
common.options.push_back("options/rebuild=true");
|
||||
common.options.push_back("global/rebuild=true");
|
||||
|
||||
if (nopull_)
|
||||
common.options.push_back("options/no_pull=true");
|
||||
@@ -971,4 +1000,35 @@ std::string cmake_command::do_doc()
|
||||
"as the one used for modorganizer projects.";
|
||||
}
|
||||
|
||||
|
||||
clipp::group inis_command::do_group()
|
||||
{
|
||||
return clipp::group(
|
||||
clipp::command("inis").set(picked_),
|
||||
|
||||
(clipp::option("-h", "--help") >> help_)
|
||||
% ("shows this message")
|
||||
);
|
||||
}
|
||||
|
||||
int inis_command::do_run()
|
||||
{
|
||||
const auto r = prepare_options(true);
|
||||
if (r != 0)
|
||||
return r;
|
||||
|
||||
u8cout << "\nhigher number overrides lower\n";
|
||||
|
||||
const auto v = inis();
|
||||
for (std::size_t i=0; i<v.size(); ++i)
|
||||
u8cout << (i + 1) << ") " << path_to_utf8(v[i]) << "\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string inis_command::do_doc()
|
||||
{
|
||||
return "Shows which INIs are found.";
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
+22
-2
@@ -17,6 +17,7 @@ public:
|
||||
std::vector<std::string> options;
|
||||
std::vector<std::string> inis;
|
||||
bool no_default_inis = false;
|
||||
bool dump_inis = false;
|
||||
std::string prefix;
|
||||
};
|
||||
|
||||
@@ -34,6 +35,8 @@ public:
|
||||
clipp::group group();
|
||||
int run();
|
||||
|
||||
const std::vector<fs::path>& inis() const;
|
||||
|
||||
protected:
|
||||
enum flags
|
||||
{
|
||||
@@ -46,14 +49,19 @@ protected:
|
||||
|
||||
command(flags f=noflags);
|
||||
|
||||
virtual void convert_cl_to_conf();
|
||||
int prepare_options(bool verbose);
|
||||
|
||||
virtual clipp::group do_group() = 0;
|
||||
virtual void do_pre_run() {};
|
||||
virtual int do_run() = 0;
|
||||
virtual std::string do_doc() { return {}; }
|
||||
|
||||
private:
|
||||
flags flags_;
|
||||
std::optional<int> code_;
|
||||
std::vector<fs::path> inis_;
|
||||
|
||||
int gather_inis(bool verbose);
|
||||
};
|
||||
|
||||
|
||||
@@ -80,6 +88,7 @@ protected:
|
||||
class options_command : public command
|
||||
{
|
||||
public:
|
||||
options_command();
|
||||
|
||||
protected:
|
||||
clipp::group do_group() override;
|
||||
@@ -94,8 +103,8 @@ public:
|
||||
build_command();
|
||||
|
||||
protected:
|
||||
void convert_cl_to_conf() override;
|
||||
clipp::group do_group() override;
|
||||
void do_pre_run() override;
|
||||
int do_run() override;
|
||||
|
||||
private:
|
||||
@@ -215,4 +224,15 @@ private:
|
||||
std::string path_;
|
||||
};
|
||||
|
||||
|
||||
class inis_command : public command
|
||||
{
|
||||
public:
|
||||
|
||||
protected:
|
||||
clipp::group do_group() override;
|
||||
int do_run() override;
|
||||
std::string do_doc() override;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
+125
-65
@@ -9,15 +9,15 @@
|
||||
namespace mob
|
||||
{
|
||||
|
||||
conf::task_map conf::map_;
|
||||
int conf::output_log_level_ = 3;
|
||||
int conf::file_log_level_ = 5;
|
||||
|
||||
std::string master_ini_filename()
|
||||
{
|
||||
return "mob.ini";
|
||||
}
|
||||
|
||||
conf::task_map conf::map_;
|
||||
int conf::output_log_level_ = 3;
|
||||
int conf::file_log_level_ = 5;
|
||||
|
||||
|
||||
std::string conf::get_global(const std::string& section, const std::string& key)
|
||||
{
|
||||
@@ -232,6 +232,18 @@ std::vector<std::string> conf::format_options()
|
||||
|
||||
std::vector<std::string> lines;
|
||||
|
||||
lines.push_back(
|
||||
pad_right("task", longest_task) + " " +
|
||||
pad_right("section", longest_section) + " " +
|
||||
pad_right("key",longest_key) + " " +
|
||||
"value");
|
||||
|
||||
lines.push_back(
|
||||
pad_right("-", longest_task, '-') + " " +
|
||||
pad_right("-", longest_section, '-') + " " +
|
||||
pad_right("-",longest_key, '-') + " " +
|
||||
"-----");
|
||||
|
||||
for (auto&& [t, ss] : map_)
|
||||
{
|
||||
for (auto&& [s, kv] : ss)
|
||||
@@ -297,25 +309,51 @@ bool try_parts(fs::path& check, const std::vector<std::string>& parts)
|
||||
return false;
|
||||
}
|
||||
|
||||
fs::path find_root_impl()
|
||||
fs::path mob_exe_path()
|
||||
{
|
||||
gcx().trace(context::conf, "looking for root directory");
|
||||
// double the buffer size 10 times
|
||||
const int max_tries = 10;
|
||||
|
||||
fs::path p = fs::current_path();
|
||||
DWORD buffer_size = MAX_PATH;
|
||||
|
||||
if (try_parts(p, {"..", "..", "..", "third-party"}))
|
||||
return p;
|
||||
for (int tries=0; tries<max_tries; ++tries)
|
||||
{
|
||||
auto buffer = std::make_unique<wchar_t[]>(buffer_size + 1);
|
||||
DWORD n = GetModuleFileNameW(0, buffer.get(), buffer_size);
|
||||
|
||||
gcx().bail_out(context::conf, "root directory not found");
|
||||
if (n == 0) {
|
||||
const auto e = GetLastError();
|
||||
gcx().bail_out(context::conf,
|
||||
"can't get module filename, {}", error_message(e));
|
||||
}
|
||||
else if (n >= buffer_size) {
|
||||
// buffer is too small, try again
|
||||
buffer_size *= 2;
|
||||
} else {
|
||||
// if GetModuleFileName() works, `n` does not include the null
|
||||
// terminator
|
||||
const std::wstring s(buffer.get(), n);
|
||||
return fs::canonical(s);
|
||||
}
|
||||
}
|
||||
|
||||
gcx().bail_out(context::conf, "can't get module filename");
|
||||
}
|
||||
|
||||
fs::path find_root()
|
||||
{
|
||||
const auto p = find_root_impl().parent_path();
|
||||
gcx().trace(context::conf, "looking for root directory");
|
||||
|
||||
gcx().trace(context::conf, "found root directory at {}", p);
|
||||
fs::path p = mob_exe_path().parent_path();
|
||||
|
||||
return p;
|
||||
if (try_parts(p, {"..", "..", "..", "third-party"}))
|
||||
{
|
||||
p = fs::canonical(p.parent_path());
|
||||
gcx().trace(context::conf, "found root directory at {}", p);
|
||||
return p;
|
||||
}
|
||||
|
||||
gcx().bail_out(context::conf, "root directory not found");
|
||||
}
|
||||
|
||||
fs::path find_in_root(const fs::path& file)
|
||||
@@ -593,17 +631,6 @@ void ini_error(const fs::path& ini, std::size_t line, const std::string& what)
|
||||
ini.filename(), (line + 1), what);
|
||||
}
|
||||
|
||||
fs::path find_master_ini()
|
||||
{
|
||||
auto p = fs::current_path();
|
||||
|
||||
if (try_parts(p, {"..", "..", "..", master_ini_filename()}))
|
||||
return fs::canonical(p);
|
||||
|
||||
gcx().bail_out(context::conf,
|
||||
"can't find master ini {}", master_ini_filename());
|
||||
}
|
||||
|
||||
std::vector<std::string> read_ini(const fs::path& ini)
|
||||
{
|
||||
std::ifstream in(ini);
|
||||
@@ -783,67 +810,100 @@ void set_special_options()
|
||||
conf::set_file_log_level(conf::get_global("global", "file_log_level"));
|
||||
}
|
||||
|
||||
std::vector<fs::path> find_inis(const std::vector<fs::path>& inis_from_cl)
|
||||
std::vector<fs::path> find_inis(
|
||||
bool auto_detect, const std::vector<std::string>& from_cl, bool verbose)
|
||||
{
|
||||
const auto master = find_master_ini();
|
||||
|
||||
std::vector<fs::path> v;
|
||||
|
||||
for (auto&& e : fs::directory_iterator(master.parent_path()))
|
||||
auto add_or_move_up = [&](fs::path p)
|
||||
{
|
||||
const auto p = e.path();
|
||||
|
||||
if (path_to_utf8(p.extension()) != ".ini")
|
||||
continue;
|
||||
|
||||
if (p.filename() == master.filename())
|
||||
continue;
|
||||
|
||||
v.push_back(p);
|
||||
}
|
||||
|
||||
std::sort(v.begin(), v.end());
|
||||
v.insert(v.begin(), master);
|
||||
|
||||
for (auto&& p : inis_from_cl)
|
||||
{
|
||||
if (!fs::exists(p))
|
||||
{
|
||||
u8cerr << "ini " << p << " not found\n";
|
||||
throw bailed();
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
|
||||
for (auto itor=v.begin(); itor!=v.end(); ++itor)
|
||||
{
|
||||
if (fs::equivalent(p, *itor))
|
||||
{
|
||||
found = true;
|
||||
v.erase(itor);
|
||||
v.push_back(p);
|
||||
break;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
v.push_back(p);
|
||||
v.push_back(p);
|
||||
};
|
||||
|
||||
|
||||
// auto detect from exe directory and cwd
|
||||
if (auto_detect)
|
||||
{
|
||||
if (verbose)
|
||||
u8cout << "root is " << path_to_utf8(find_root()) << "\n";
|
||||
|
||||
const auto master = find_in_root(master_ini_filename());
|
||||
|
||||
if (verbose)
|
||||
u8cout << "found master " << master_ini_filename() << "\n";
|
||||
|
||||
v.push_back(master);
|
||||
|
||||
const auto in_cwd = fs::current_path() / master_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(fs::canonical(in_cwd));
|
||||
}
|
||||
}
|
||||
|
||||
// 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(p);
|
||||
}
|
||||
}
|
||||
|
||||
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(p);
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
void init_options(
|
||||
const std::vector<fs::path>& inis_from_cl, bool auto_detection,
|
||||
const std::vector<std::string>& opts)
|
||||
const std::vector<fs::path>& inis, const std::vector<std::string>& opts)
|
||||
{
|
||||
std::vector<fs::path> inis;
|
||||
|
||||
if (auto_detection)
|
||||
inis = find_inis(inis_from_cl);
|
||||
else
|
||||
inis = inis_from_cl;
|
||||
|
||||
MOB_ASSERT(!inis.empty());
|
||||
|
||||
bool add = true;
|
||||
|
||||
+5
-2
@@ -103,9 +103,12 @@ struct paths
|
||||
|
||||
std::string master_ini_filename();
|
||||
|
||||
std::vector<fs::path> find_inis(
|
||||
bool auto_detect, const std::vector<std::string>& from_cl,
|
||||
bool verbose);
|
||||
|
||||
void init_options(
|
||||
const std::vector<fs::path>& inis_from_cl, bool auto_detection,
|
||||
const std::vector<std::string>& opts);
|
||||
const std::vector<fs::path>& inis, const std::vector<std::string>& opts);
|
||||
|
||||
bool verify_options();
|
||||
void log_options();
|
||||
|
||||
+20
-7
@@ -224,14 +224,14 @@ void this_env::set(const std::string& k, const std::string& v, env::flags f)
|
||||
|
||||
case env::append:
|
||||
{
|
||||
const std::wstring current = get_impl(k);
|
||||
const std::wstring current = get_impl(k).value_or(L"");
|
||||
::SetEnvironmentVariableW(wk.c_str(), (current + wv).c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
case env::prepend:
|
||||
{
|
||||
const std::wstring current = get_impl(k);
|
||||
const std::wstring current = get_impl(k).value_or(L"");
|
||||
::SetEnvironmentVariableW(wk.c_str(), (wv + current).c_str());
|
||||
break;
|
||||
}
|
||||
@@ -246,10 +246,23 @@ void this_env::prepend_to_path(const fs::path& p)
|
||||
|
||||
std::string this_env::get(const std::string& name)
|
||||
{
|
||||
return utf16_to_utf8(get_impl(name));
|
||||
auto v = get_impl(name);
|
||||
if (!v)
|
||||
bail_out("environment variable {} doesn't exist", name);
|
||||
|
||||
return utf16_to_utf8(*v);
|
||||
}
|
||||
|
||||
std::wstring this_env::get_impl(const std::string& k)
|
||||
std::optional<std::string> this_env::get_opt(const std::string& name)
|
||||
{
|
||||
auto v = get_impl(name);
|
||||
if (v)
|
||||
return utf16_to_utf8(*v);
|
||||
else
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<std::wstring> this_env::get_impl(const std::string& k)
|
||||
{
|
||||
const std::wstring wk = utf8_to_utf16(k);
|
||||
|
||||
@@ -257,7 +270,7 @@ std::wstring this_env::get_impl(const std::string& k)
|
||||
wk.c_str(), nullptr, 0);
|
||||
|
||||
if (buffer_size == 0)
|
||||
bail_out("environment variable {} doesn't exist", k);
|
||||
return {};
|
||||
|
||||
auto buffer = std::make_unique<wchar_t[]>(buffer_size + 1);
|
||||
std::fill(buffer.get(), buffer.get() + buffer_size + 1, 0);
|
||||
@@ -266,11 +279,11 @@ std::wstring this_env::get_impl(const std::string& k)
|
||||
wk.c_str(), buffer.get(), static_cast<DWORD>(buffer_size));
|
||||
|
||||
if (written == 0)
|
||||
bail_out("environment variable {} doesn't exist", k);
|
||||
return {};
|
||||
|
||||
MOB_ASSERT((written + 1) == buffer_size);
|
||||
|
||||
return {buffer.get(), buffer.get() + written};
|
||||
return std::wstring(buffer.get(), buffer.get() + written);
|
||||
}
|
||||
|
||||
env this_env::get()
|
||||
|
||||
@@ -50,10 +50,12 @@ struct this_env
|
||||
static void prepend_to_path(const fs::path& p);
|
||||
|
||||
static env get();
|
||||
|
||||
static std::string get(const std::string& k);
|
||||
static std::optional<std::string> get_opt(const std::string& k);
|
||||
|
||||
private:
|
||||
static std::wstring get_impl(const std::string& k);
|
||||
static std::optional<std::wstring> get_impl(const std::string& k);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
+2
-1
@@ -24,7 +24,8 @@ std::shared_ptr<command> handle_command_line(const std::vector<std::string>& arg
|
||||
std::make_unique<list_command>(),
|
||||
std::make_unique<release_command>(),
|
||||
std::make_unique<git_command>(),
|
||||
std::make_unique<cmake_command>()
|
||||
std::make_unique<cmake_command>(),
|
||||
std::make_unique<inis_command>()
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -322,6 +322,27 @@ std::string join(const std::vector<std::string>& v, const std::string& sep)
|
||||
return s;
|
||||
}
|
||||
|
||||
std::vector<std::string> split(const std::string& s, const std::string& seps)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
std::size_t start = 0;
|
||||
|
||||
while (start < s.size())
|
||||
{
|
||||
auto p = s.find_first_of(seps, start);
|
||||
if (p == std::string::npos)
|
||||
p = s.size();
|
||||
|
||||
if (p - start > 0)
|
||||
v.push_back(s.substr(start, p - start));
|
||||
|
||||
start = p + 1;
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
void trim(std::string& s, const std::string& what)
|
||||
{
|
||||
while (!s.empty())
|
||||
|
||||
@@ -232,6 +232,8 @@ std::string replace_all(
|
||||
std::string s, const std::string& from, const std::string& to);
|
||||
|
||||
std::string join(const std::vector<std::string>& v, const std::string& sep);
|
||||
std::vector<std::string> split(const std::string& s, const std::string& sep);
|
||||
|
||||
std::string pad_right(std::string s, std::size_t n, char c=' ');
|
||||
std::string pad_left(std::string s, std::size_t n, char c=' ');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user