mirror of
https://github.com/ModOrganizer2/mob.git
synced 2026-07-27 14:07:05 -07:00
split string stuff and assert
This commit is contained in:
-425
@@ -151,209 +151,6 @@ url make_appveyor_artifact_url(
|
||||
project + "/artifacts/" + filename + "?job=Platform:%20" + arch_s;
|
||||
}
|
||||
|
||||
bool glob_match(const std::string& pattern, const std::string& s)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::string fixed_pattern = pattern;
|
||||
fixed_pattern = replace_all(fixed_pattern, "*", ".*");
|
||||
fixed_pattern = replace_all(fixed_pattern, "_", "-");
|
||||
|
||||
std::string fixed_string = s;
|
||||
fixed_string = replace_all(fixed_string, "_", "-");
|
||||
|
||||
std::regex re(fixed_pattern, std::regex::icase);
|
||||
|
||||
return std::regex_match(fixed_string, re);
|
||||
}
|
||||
catch(std::exception&)
|
||||
{
|
||||
u8cerr
|
||||
<< "bad glob '" << pattern << "'\n"
|
||||
<< "globs are actually bastardized regexes where '*' is "
|
||||
<< "replaced by '.*', so don't push it\n";
|
||||
|
||||
throw bailed();
|
||||
}
|
||||
}
|
||||
|
||||
std::string replace_all(
|
||||
std::string s, const std::string& from, const std::string& to)
|
||||
{
|
||||
std::size_t start = 0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
const auto pos = s.find(from, start);
|
||||
if (pos == std::string::npos)
|
||||
break;
|
||||
|
||||
s.replace(pos, from.size(), to);
|
||||
start = pos + to.size();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
std::vector<std::string> split_quoted(const std::string& s, const std::string& seps)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
bool q = false;
|
||||
std::string token;
|
||||
|
||||
for (std::size_t i=0; i<s.size(); ++i)
|
||||
{
|
||||
if (seps.find(s[i]) != std::string::npos)
|
||||
{
|
||||
if (q)
|
||||
{
|
||||
token += s[i];
|
||||
}
|
||||
else if (!token.empty())
|
||||
{
|
||||
v.push_back(token);
|
||||
token = "";
|
||||
}
|
||||
}
|
||||
else if (s[i] == '"')
|
||||
{
|
||||
if (q)
|
||||
{
|
||||
q = false;
|
||||
|
||||
if (!token.empty())
|
||||
{
|
||||
v.push_back(token);
|
||||
token = "";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
q = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
token += s[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (!token.empty())
|
||||
v.push_back(token);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
template <class C>
|
||||
void trim_impl(std::basic_string<C>& s, std::basic_string_view<C> what)
|
||||
{
|
||||
while (!s.empty())
|
||||
{
|
||||
if (what.find(s[0]) != std::string::npos)
|
||||
s.erase(0, 1);
|
||||
else if (what.find(s[s.size() - 1]) != std::string::npos)
|
||||
s.erase(s.size() - 1, 1);
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template <class C>
|
||||
std::basic_string<C> trim_copy_impl(
|
||||
std::basic_string_view<C> s, std::basic_string_view<C> what)
|
||||
{
|
||||
std::basic_string<C> c(s);
|
||||
trim(c, what);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
void trim(std::string& s, std::string_view what)
|
||||
{
|
||||
trim_impl(s, what);
|
||||
}
|
||||
|
||||
void trim(std::wstring& s, std::wstring_view what)
|
||||
{
|
||||
trim_impl(s, what);
|
||||
}
|
||||
|
||||
std::string trim_copy(std::string_view s, std::string_view what)
|
||||
{
|
||||
return trim_copy_impl(s, what);
|
||||
}
|
||||
|
||||
std::wstring trim_copy(std::wstring_view s, std::wstring_view what)
|
||||
{
|
||||
return trim_copy_impl(s, what);
|
||||
}
|
||||
|
||||
|
||||
std::string pad_right(std::string s, std::size_t n, char c)
|
||||
{
|
||||
if (s.size() < n)
|
||||
s.append(n - s.size() , c);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string pad_left(std::string s, std::size_t n, char c)
|
||||
{
|
||||
if (s.size() < n)
|
||||
s.insert(s.begin(), n - s.size() , c);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
std::string table(
|
||||
const std::vector<std::pair<std::string, std::string>>& v,
|
||||
std::size_t indent, std::size_t spacing)
|
||||
{
|
||||
std::size_t longest = 0;
|
||||
|
||||
for (auto&& p : v)
|
||||
longest = std::max(longest, p.first.size());
|
||||
|
||||
std::string s;
|
||||
|
||||
for (auto&& p : v)
|
||||
{
|
||||
if (!s.empty())
|
||||
s += "\n";
|
||||
|
||||
s +=
|
||||
std::string(indent, ' ') +
|
||||
pad_right(p.first, longest) + " " +
|
||||
std::string(spacing, ' ') +
|
||||
p.second;
|
||||
}
|
||||
|
||||
return s;
|
||||
|
||||
}
|
||||
|
||||
|
||||
file_deleter::file_deleter(const context& cx, fs::path p)
|
||||
: cx_(cx), p_(std::move(p)), delete_(true)
|
||||
@@ -577,227 +374,5 @@ console_color::~console_color()
|
||||
}
|
||||
|
||||
|
||||
std::optional<std::wstring> to_widechar(UINT from, std::string_view s)
|
||||
{
|
||||
std::wstring ws;
|
||||
|
||||
if (s.empty())
|
||||
return ws;
|
||||
|
||||
ws.resize(s.size() + 1);
|
||||
|
||||
for (int t=0; t<3; ++t)
|
||||
{
|
||||
const int written = MultiByteToWideChar(
|
||||
from, 0, s.data(), static_cast<int>(s.size()),
|
||||
ws.data(), static_cast<int>(ws.size()));
|
||||
|
||||
if (written <= 0)
|
||||
{
|
||||
const auto e = GetLastError();
|
||||
|
||||
if (e == ERROR_INSUFFICIENT_BUFFER)
|
||||
{
|
||||
ws.resize(ws.size() * 2);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return {};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MOB_ASSERT(static_cast<std::size_t>(written) <= s.size());
|
||||
ws.resize(static_cast<std::size_t>(written));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ws;
|
||||
}
|
||||
|
||||
std::optional<std::string> to_multibyte(UINT to, std::wstring_view ws)
|
||||
{
|
||||
std::string s;
|
||||
|
||||
if (ws.empty())
|
||||
return s;
|
||||
|
||||
|
||||
s.resize(static_cast<std::size_t>(
|
||||
static_cast<double>(ws.size()) * 1.5));
|
||||
|
||||
for (int t=0; t<3; ++t)
|
||||
{
|
||||
const int written = WideCharToMultiByte(
|
||||
to, 0, ws.data(), static_cast<int>(ws.size()),
|
||||
s.data(), static_cast<int>(s.size()), nullptr, nullptr);
|
||||
|
||||
if (written <= 0)
|
||||
{
|
||||
const auto e = GetLastError();
|
||||
|
||||
if (e == ERROR_INSUFFICIENT_BUFFER)
|
||||
{
|
||||
s.resize(ws.size() * 2);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return {};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MOB_ASSERT(static_cast<std::size_t>(written) <= s.size());
|
||||
s.resize(static_cast<std::size_t>(written));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
std::wstring utf8_to_utf16(std::string_view s)
|
||||
{
|
||||
auto ws = to_widechar(CP_UTF8, s);
|
||||
if (!ws)
|
||||
{
|
||||
std::wcerr << L"can't convert from utf8 to utf16\n";
|
||||
return L"???";
|
||||
}
|
||||
|
||||
return std::move(*ws);
|
||||
}
|
||||
|
||||
std::string utf16_to_utf8(std::wstring_view ws)
|
||||
{
|
||||
auto s = to_multibyte(CP_UTF8, ws);
|
||||
if (!s)
|
||||
{
|
||||
std::wcerr << L"can't convert from utf16 to utf8\n";
|
||||
return "???";
|
||||
}
|
||||
|
||||
return std::move(*s);
|
||||
}
|
||||
|
||||
std::wstring cp_to_utf16(UINT from, std::string_view s)
|
||||
{
|
||||
auto ws = to_widechar(from, s);
|
||||
if (!ws)
|
||||
{
|
||||
std::wcerr << L"can't convert from cp " << from << L" to utf16\n";
|
||||
return L"???";
|
||||
}
|
||||
|
||||
return std::move(*ws);
|
||||
}
|
||||
|
||||
std::string utf16_to_cp(UINT to, std::wstring_view ws)
|
||||
{
|
||||
auto s = to_multibyte(to, ws);
|
||||
|
||||
if (!s)
|
||||
{
|
||||
std::wcerr << L"can't convert from cp " << to << L" to utf16\n";
|
||||
return "???";
|
||||
}
|
||||
|
||||
return std::move(*s);
|
||||
}
|
||||
|
||||
|
||||
std::string bytes_to_utf8(encodings e, std::string_view s)
|
||||
{
|
||||
switch (e)
|
||||
{
|
||||
case encodings::utf16:
|
||||
{
|
||||
const auto* ws = reinterpret_cast<const wchar_t*>(s.data());
|
||||
const auto chars = s.size() / sizeof(wchar_t);
|
||||
return utf16_to_utf8({ws, chars});
|
||||
}
|
||||
|
||||
case encodings::acp:
|
||||
{
|
||||
const std::wstring utf16 = cp_to_utf16(CP_ACP, s);
|
||||
return utf16_to_utf8(utf16);
|
||||
}
|
||||
|
||||
case encodings::oem:
|
||||
{
|
||||
const std::wstring utf16 = cp_to_utf16(CP_OEMCP, s);
|
||||
return utf16_to_utf8(utf16);
|
||||
}
|
||||
|
||||
case encodings::utf8:
|
||||
case encodings::dont_know:
|
||||
default:
|
||||
{
|
||||
return {s.begin(), s.end()};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string utf16_to_bytes(encodings e, std::wstring_view ws)
|
||||
{
|
||||
switch (e)
|
||||
{
|
||||
case encodings::utf16:
|
||||
{
|
||||
return std::string(
|
||||
reinterpret_cast<const char*>(ws.data()),
|
||||
ws.size() * sizeof(wchar_t));
|
||||
}
|
||||
|
||||
case encodings::acp:
|
||||
{
|
||||
return utf16_to_cp(CP_ACP, ws);
|
||||
}
|
||||
|
||||
case encodings::oem:
|
||||
{
|
||||
return utf16_to_cp(CP_OEMCP, ws);
|
||||
}
|
||||
|
||||
case encodings::utf8:
|
||||
case encodings::dont_know:
|
||||
default:
|
||||
{
|
||||
return utf16_to_utf8(ws);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::string utf8_to_bytes(encodings e, std::string_view utf8)
|
||||
{
|
||||
switch (e)
|
||||
{
|
||||
case encodings::utf16:
|
||||
case encodings::acp:
|
||||
case encodings::oem:
|
||||
{
|
||||
const std::wstring ws = utf8_to_utf16(utf8);
|
||||
return utf16_to_bytes(e, ws);
|
||||
}
|
||||
|
||||
case encodings::utf8:
|
||||
case encodings::dont_know:
|
||||
default:
|
||||
{
|
||||
return std::string(utf8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::string path_to_utf8(fs::path p)
|
||||
{
|
||||
return utf16_to_utf8(p.native());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
+2
-138
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "utility/string.h"
|
||||
|
||||
namespace mob
|
||||
{
|
||||
|
||||
@@ -23,45 +25,6 @@ bool is_any_set(E e, E v)
|
||||
}
|
||||
|
||||
|
||||
#define MOB_WIDEN2(x) L ## x
|
||||
#define MOB_WIDEN(x) MOB_WIDEN2(x)
|
||||
#define MOB_FILE_UTF16 MOB_WIDEN(__FILE__)
|
||||
|
||||
#define MOB_ASSERT(x, ...) \
|
||||
mob_assert(x, __VA_ARGS__, #x, MOB_FILE_UTF16, __LINE__, __FUNCSIG__);
|
||||
|
||||
void mob_assertion_failed(
|
||||
const char* message,
|
||||
const char* exp, const wchar_t* file, int line, const char* func);
|
||||
|
||||
template <class X>
|
||||
inline void mob_assert(
|
||||
X&& x, const char* message,
|
||||
const char* exp, const wchar_t* file, int line, const char* func)
|
||||
{
|
||||
if (!(x))
|
||||
mob_assertion_failed(message, exp, file, line, func);
|
||||
}
|
||||
|
||||
template <class X>
|
||||
inline void mob_assert(
|
||||
X&& x, const char* exp, const wchar_t* file, int line, const char* func)
|
||||
{
|
||||
if (!(x))
|
||||
mob_assertion_failed(nullptr, exp, file, line, func);
|
||||
}
|
||||
|
||||
|
||||
enum class encodings
|
||||
{
|
||||
dont_know = 0,
|
||||
utf8,
|
||||
utf16,
|
||||
acp,
|
||||
oem
|
||||
};
|
||||
|
||||
|
||||
class context;
|
||||
class url;
|
||||
|
||||
@@ -298,58 +261,6 @@ url make_prebuilt_url(const std::string& filename);
|
||||
url make_appveyor_artifact_url(
|
||||
arch a, const std::string& project, const std::string& filename);
|
||||
|
||||
// case insensitive, underscores and dashes are equivalent; gets converted to
|
||||
// a regex where * becomes .*
|
||||
//
|
||||
bool glob_match(const std::string& pattern, const std::string& s);
|
||||
|
||||
std::string replace_all(
|
||||
std::string s, const std::string& from, const std::string& to);
|
||||
|
||||
template <class T, class Sep>
|
||||
T join(const std::vector<T>& v, const Sep& sep)
|
||||
{
|
||||
T s;
|
||||
bool first = true;
|
||||
|
||||
for (auto&& e : v)
|
||||
{
|
||||
if (!first)
|
||||
s += sep;
|
||||
|
||||
s += e;
|
||||
first = false;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
std::vector<std::string> split(const std::string& s, const std::string& seps);
|
||||
std::vector<std::string> split_quoted(const std::string& s, const std::string& seps);
|
||||
|
||||
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=' ');
|
||||
|
||||
void trim(std::string& s, std::string_view what=" \t\r\n");
|
||||
void trim(std::wstring& s, std::wstring_view what=L" \t\r\n");
|
||||
|
||||
std::string table(
|
||||
const std::vector<std::pair<std::string, std::string>>& v,
|
||||
std::size_t indent, std::size_t spacing);
|
||||
|
||||
std::string trim_copy(std::string_view s, std::string_view what=" \t\r\n");
|
||||
std::wstring trim_copy(std::wstring_view s, std::wstring_view what=L" \t\r\n");
|
||||
|
||||
std::wstring utf8_to_utf16(std::string_view s);
|
||||
std::string utf16_to_utf8(std::wstring_view ws);
|
||||
std::string bytes_to_utf8(encodings e, std::string_view bytes);
|
||||
std::string utf8_to_bytes(encodings e, std::string_view utf8);
|
||||
|
||||
template <class T>
|
||||
std::string path_to_utf8(T&&) = delete;
|
||||
|
||||
std::string path_to_utf8(fs::path p);
|
||||
|
||||
|
||||
class u8stream
|
||||
{
|
||||
@@ -386,53 +297,6 @@ void set_std_streams();
|
||||
std::mutex& global_output_mutex();
|
||||
|
||||
|
||||
template <class F>
|
||||
void for_each_line(std::string_view s, F&& f)
|
||||
{
|
||||
if (s.empty())
|
||||
return;
|
||||
|
||||
const char* const begin = s.data();
|
||||
const char* const end = s.data() + s.size();
|
||||
|
||||
const char* start = begin;
|
||||
const char* p = begin;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
MOB_ASSERT(p && p >= begin && p <= end);
|
||||
MOB_ASSERT(start && start >= begin && start <= end);
|
||||
|
||||
if (p == end || *p == '\n' || *p == '\r')
|
||||
{
|
||||
if (p != start)
|
||||
{
|
||||
MOB_ASSERT(p >= start);
|
||||
|
||||
const auto n = static_cast<std::size_t>(p - start);
|
||||
MOB_ASSERT(n <= s.size());
|
||||
|
||||
f(std::string_view(start, n));
|
||||
}
|
||||
|
||||
while (p != end && (*p == '\n' || *p == '\r'))
|
||||
++p;
|
||||
|
||||
MOB_ASSERT(p && p >= begin && p <= end);
|
||||
|
||||
if (p == end)
|
||||
break;
|
||||
|
||||
start = p;
|
||||
}
|
||||
else
|
||||
{
|
||||
++p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <class T>
|
||||
class repeat_iterator
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
namespace mob
|
||||
{
|
||||
|
||||
#define MOB_WIDEN2(x) L ## x
|
||||
#define MOB_WIDEN(x) MOB_WIDEN2(x)
|
||||
#define MOB_FILE_UTF16 MOB_WIDEN(__FILE__)
|
||||
|
||||
#define MOB_ASSERT(x, ...) \
|
||||
mob_assert(x, __VA_ARGS__, #x, MOB_FILE_UTF16, __LINE__, __FUNCSIG__);
|
||||
|
||||
void mob_assertion_failed(
|
||||
const char* message,
|
||||
const char* exp, const wchar_t* file, int line, const char* func);
|
||||
|
||||
template <class X>
|
||||
inline void mob_assert(
|
||||
X&& x, const char* message,
|
||||
const char* exp, const wchar_t* file, int line, const char* func)
|
||||
{
|
||||
if (!(x))
|
||||
mob_assertion_failed(message, exp, file, line, func);
|
||||
}
|
||||
|
||||
template <class X>
|
||||
inline void mob_assert(
|
||||
X&& x, const char* exp, const wchar_t* file, int line, const char* func)
|
||||
{
|
||||
if (!(x))
|
||||
mob_assertion_failed(nullptr, exp, file, line, func);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,434 @@
|
||||
#include "pch.h"
|
||||
#include "string.h"
|
||||
#include "../utility.h"
|
||||
|
||||
namespace mob
|
||||
{
|
||||
|
||||
bool glob_match(const std::string& pattern, const std::string& s)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::string fixed_pattern = pattern;
|
||||
fixed_pattern = replace_all(fixed_pattern, "*", ".*");
|
||||
fixed_pattern = replace_all(fixed_pattern, "_", "-");
|
||||
|
||||
std::string fixed_string = s;
|
||||
fixed_string = replace_all(fixed_string, "_", "-");
|
||||
|
||||
std::regex re(fixed_pattern, std::regex::icase);
|
||||
|
||||
return std::regex_match(fixed_string, re);
|
||||
}
|
||||
catch(std::exception&)
|
||||
{
|
||||
u8cerr
|
||||
<< "bad glob '" << pattern << "'\n"
|
||||
<< "globs are actually bastardized regexes where '*' is "
|
||||
<< "replaced by '.*', so don't push it\n";
|
||||
|
||||
throw bailed();
|
||||
}
|
||||
}
|
||||
|
||||
std::string replace_all(
|
||||
std::string s, const std::string& from, const std::string& to)
|
||||
{
|
||||
std::size_t start = 0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
const auto pos = s.find(from, start);
|
||||
if (pos == std::string::npos)
|
||||
break;
|
||||
|
||||
s.replace(pos, from.size(), to);
|
||||
start = pos + to.size();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
std::vector<std::string> split_quoted(const std::string& s, const std::string& seps)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
bool q = false;
|
||||
std::string token;
|
||||
|
||||
for (std::size_t i=0; i<s.size(); ++i)
|
||||
{
|
||||
if (seps.find(s[i]) != std::string::npos)
|
||||
{
|
||||
if (q)
|
||||
{
|
||||
token += s[i];
|
||||
}
|
||||
else if (!token.empty())
|
||||
{
|
||||
v.push_back(token);
|
||||
token = "";
|
||||
}
|
||||
}
|
||||
else if (s[i] == '"')
|
||||
{
|
||||
if (q)
|
||||
{
|
||||
q = false;
|
||||
|
||||
if (!token.empty())
|
||||
{
|
||||
v.push_back(token);
|
||||
token = "";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
q = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
token += s[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (!token.empty())
|
||||
v.push_back(token);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
template <class C>
|
||||
void trim_impl(std::basic_string<C>& s, std::basic_string_view<C> what)
|
||||
{
|
||||
while (!s.empty())
|
||||
{
|
||||
if (what.find(s[0]) != std::string::npos)
|
||||
s.erase(0, 1);
|
||||
else if (what.find(s[s.size() - 1]) != std::string::npos)
|
||||
s.erase(s.size() - 1, 1);
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template <class C>
|
||||
std::basic_string<C> trim_copy_impl(
|
||||
std::basic_string_view<C> s, std::basic_string_view<C> what)
|
||||
{
|
||||
std::basic_string<C> c(s);
|
||||
trim(c, what);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
void trim(std::string& s, std::string_view what)
|
||||
{
|
||||
trim_impl(s, what);
|
||||
}
|
||||
|
||||
void trim(std::wstring& s, std::wstring_view what)
|
||||
{
|
||||
trim_impl(s, what);
|
||||
}
|
||||
|
||||
std::string trim_copy(std::string_view s, std::string_view what)
|
||||
{
|
||||
return trim_copy_impl(s, what);
|
||||
}
|
||||
|
||||
std::wstring trim_copy(std::wstring_view s, std::wstring_view what)
|
||||
{
|
||||
return trim_copy_impl(s, what);
|
||||
}
|
||||
|
||||
|
||||
std::string pad_right(std::string s, std::size_t n, char c)
|
||||
{
|
||||
if (s.size() < n)
|
||||
s.append(n - s.size() , c);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string pad_left(std::string s, std::size_t n, char c)
|
||||
{
|
||||
if (s.size() < n)
|
||||
s.insert(s.begin(), n - s.size() , c);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
std::string table(
|
||||
const std::vector<std::pair<std::string, std::string>>& v,
|
||||
std::size_t indent, std::size_t spacing)
|
||||
{
|
||||
std::size_t longest = 0;
|
||||
|
||||
for (auto&& p : v)
|
||||
longest = std::max(longest, p.first.size());
|
||||
|
||||
std::string s;
|
||||
|
||||
for (auto&& p : v)
|
||||
{
|
||||
if (!s.empty())
|
||||
s += "\n";
|
||||
|
||||
s +=
|
||||
std::string(indent, ' ') +
|
||||
pad_right(p.first, longest) + " " +
|
||||
std::string(spacing, ' ') +
|
||||
p.second;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
std::optional<std::wstring> to_widechar(UINT from, std::string_view s)
|
||||
{
|
||||
std::wstring ws;
|
||||
|
||||
if (s.empty())
|
||||
return ws;
|
||||
|
||||
ws.resize(s.size() + 1);
|
||||
|
||||
for (int t=0; t<3; ++t)
|
||||
{
|
||||
const int written = MultiByteToWideChar(
|
||||
from, 0, s.data(), static_cast<int>(s.size()),
|
||||
ws.data(), static_cast<int>(ws.size()));
|
||||
|
||||
if (written <= 0)
|
||||
{
|
||||
const auto e = GetLastError();
|
||||
|
||||
if (e == ERROR_INSUFFICIENT_BUFFER)
|
||||
{
|
||||
ws.resize(ws.size() * 2);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return {};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MOB_ASSERT(static_cast<std::size_t>(written) <= s.size());
|
||||
ws.resize(static_cast<std::size_t>(written));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ws;
|
||||
}
|
||||
|
||||
std::optional<std::string> to_multibyte(UINT to, std::wstring_view ws)
|
||||
{
|
||||
std::string s;
|
||||
|
||||
if (ws.empty())
|
||||
return s;
|
||||
|
||||
|
||||
s.resize(static_cast<std::size_t>(
|
||||
static_cast<double>(ws.size()) * 1.5));
|
||||
|
||||
for (int t=0; t<3; ++t)
|
||||
{
|
||||
const int written = WideCharToMultiByte(
|
||||
to, 0, ws.data(), static_cast<int>(ws.size()),
|
||||
s.data(), static_cast<int>(s.size()), nullptr, nullptr);
|
||||
|
||||
if (written <= 0)
|
||||
{
|
||||
const auto e = GetLastError();
|
||||
|
||||
if (e == ERROR_INSUFFICIENT_BUFFER)
|
||||
{
|
||||
s.resize(ws.size() * 2);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return {};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MOB_ASSERT(static_cast<std::size_t>(written) <= s.size());
|
||||
s.resize(static_cast<std::size_t>(written));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
std::wstring utf8_to_utf16(std::string_view s)
|
||||
{
|
||||
auto ws = to_widechar(CP_UTF8, s);
|
||||
if (!ws)
|
||||
{
|
||||
std::wcerr << L"can't convert from utf8 to utf16\n";
|
||||
return L"???";
|
||||
}
|
||||
|
||||
return std::move(*ws);
|
||||
}
|
||||
|
||||
std::string utf16_to_utf8(std::wstring_view ws)
|
||||
{
|
||||
auto s = to_multibyte(CP_UTF8, ws);
|
||||
if (!s)
|
||||
{
|
||||
std::wcerr << L"can't convert from utf16 to utf8\n";
|
||||
return "???";
|
||||
}
|
||||
|
||||
return std::move(*s);
|
||||
}
|
||||
|
||||
std::wstring cp_to_utf16(UINT from, std::string_view s)
|
||||
{
|
||||
auto ws = to_widechar(from, s);
|
||||
if (!ws)
|
||||
{
|
||||
std::wcerr << L"can't convert from cp " << from << L" to utf16\n";
|
||||
return L"???";
|
||||
}
|
||||
|
||||
return std::move(*ws);
|
||||
}
|
||||
|
||||
std::string utf16_to_cp(UINT to, std::wstring_view ws)
|
||||
{
|
||||
auto s = to_multibyte(to, ws);
|
||||
|
||||
if (!s)
|
||||
{
|
||||
std::wcerr << L"can't convert from cp " << to << L" to utf16\n";
|
||||
return "???";
|
||||
}
|
||||
|
||||
return std::move(*s);
|
||||
}
|
||||
|
||||
|
||||
std::string bytes_to_utf8(encodings e, std::string_view s)
|
||||
{
|
||||
switch (e)
|
||||
{
|
||||
case encodings::utf16:
|
||||
{
|
||||
const auto* ws = reinterpret_cast<const wchar_t*>(s.data());
|
||||
const auto chars = s.size() / sizeof(wchar_t);
|
||||
return utf16_to_utf8({ws, chars});
|
||||
}
|
||||
|
||||
case encodings::acp:
|
||||
{
|
||||
const std::wstring utf16 = cp_to_utf16(CP_ACP, s);
|
||||
return utf16_to_utf8(utf16);
|
||||
}
|
||||
|
||||
case encodings::oem:
|
||||
{
|
||||
const std::wstring utf16 = cp_to_utf16(CP_OEMCP, s);
|
||||
return utf16_to_utf8(utf16);
|
||||
}
|
||||
|
||||
case encodings::utf8:
|
||||
case encodings::dont_know:
|
||||
default:
|
||||
{
|
||||
return {s.begin(), s.end()};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string utf16_to_bytes(encodings e, std::wstring_view ws)
|
||||
{
|
||||
switch (e)
|
||||
{
|
||||
case encodings::utf16:
|
||||
{
|
||||
return std::string(
|
||||
reinterpret_cast<const char*>(ws.data()),
|
||||
ws.size() * sizeof(wchar_t));
|
||||
}
|
||||
|
||||
case encodings::acp:
|
||||
{
|
||||
return utf16_to_cp(CP_ACP, ws);
|
||||
}
|
||||
|
||||
case encodings::oem:
|
||||
{
|
||||
return utf16_to_cp(CP_OEMCP, ws);
|
||||
}
|
||||
|
||||
case encodings::utf8:
|
||||
case encodings::dont_know:
|
||||
default:
|
||||
{
|
||||
return utf16_to_utf8(ws);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
std::string utf8_to_bytes(encodings e, std::string_view utf8)
|
||||
{
|
||||
switch (e)
|
||||
{
|
||||
case encodings::utf16:
|
||||
case encodings::acp:
|
||||
case encodings::oem:
|
||||
{
|
||||
const std::wstring ws = utf8_to_utf16(utf8);
|
||||
return utf16_to_bytes(e, ws);
|
||||
}
|
||||
|
||||
case encodings::utf8:
|
||||
case encodings::dont_know:
|
||||
default:
|
||||
{
|
||||
return std::string(utf8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::string path_to_utf8(fs::path p)
|
||||
{
|
||||
return utf16_to_utf8(p.native());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,117 @@
|
||||
#pragma once
|
||||
|
||||
#include "assert.h"
|
||||
|
||||
namespace mob
|
||||
{
|
||||
|
||||
enum class encodings
|
||||
{
|
||||
dont_know = 0,
|
||||
utf8,
|
||||
utf16,
|
||||
acp,
|
||||
oem
|
||||
};
|
||||
|
||||
|
||||
// case insensitive, underscores and dashes are equivalent; gets converted to
|
||||
// a regex where * becomes .*
|
||||
//
|
||||
bool glob_match(const std::string& pattern, const std::string& s);
|
||||
|
||||
std::string replace_all(
|
||||
std::string s, const std::string& from, const std::string& to);
|
||||
|
||||
template <class T, class Sep>
|
||||
T join(const std::vector<T>& v, const Sep& sep)
|
||||
{
|
||||
T s;
|
||||
bool first = true;
|
||||
|
||||
for (auto&& e : v)
|
||||
{
|
||||
if (!first)
|
||||
s += sep;
|
||||
|
||||
s += e;
|
||||
first = false;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
std::vector<std::string> split(const std::string& s, const std::string& seps);
|
||||
std::vector<std::string> split_quoted(const std::string& s, const std::string& seps);
|
||||
|
||||
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=' ');
|
||||
|
||||
void trim(std::string& s, std::string_view what=" \t\r\n");
|
||||
void trim(std::wstring& s, std::wstring_view what=L" \t\r\n");
|
||||
|
||||
std::string table(
|
||||
const std::vector<std::pair<std::string, std::string>>& v,
|
||||
std::size_t indent, std::size_t spacing);
|
||||
|
||||
std::string trim_copy(std::string_view s, std::string_view what=" \t\r\n");
|
||||
std::wstring trim_copy(std::wstring_view s, std::wstring_view what=L" \t\r\n");
|
||||
|
||||
std::wstring utf8_to_utf16(std::string_view s);
|
||||
std::string utf16_to_utf8(std::wstring_view ws);
|
||||
std::string bytes_to_utf8(encodings e, std::string_view bytes);
|
||||
std::string utf8_to_bytes(encodings e, std::string_view utf8);
|
||||
|
||||
template <class T>
|
||||
std::string path_to_utf8(T&&) = delete;
|
||||
|
||||
std::string path_to_utf8(fs::path p);
|
||||
|
||||
|
||||
template <class F>
|
||||
void for_each_line(std::string_view s, F&& f)
|
||||
{
|
||||
if (s.empty())
|
||||
return;
|
||||
|
||||
const char* const begin = s.data();
|
||||
const char* const end = s.data() + s.size();
|
||||
|
||||
const char* start = begin;
|
||||
const char* p = begin;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
MOB_ASSERT(p && p >= begin && p <= end);
|
||||
MOB_ASSERT(start && start >= begin && start <= end);
|
||||
|
||||
if (p == end || *p == '\n' || *p == '\r')
|
||||
{
|
||||
if (p != start)
|
||||
{
|
||||
MOB_ASSERT(p >= start);
|
||||
|
||||
const auto n = static_cast<std::size_t>(p - start);
|
||||
MOB_ASSERT(n <= s.size());
|
||||
|
||||
f(std::string_view(start, n));
|
||||
}
|
||||
|
||||
while (p != end && (*p == '\n' || *p == '\r'))
|
||||
++p;
|
||||
|
||||
MOB_ASSERT(p && p >= begin && p <= end);
|
||||
|
||||
if (p == end)
|
||||
break;
|
||||
|
||||
start = p;
|
||||
}
|
||||
else
|
||||
{
|
||||
++p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -106,6 +106,7 @@
|
||||
<ClCompile Include="..\src\tools\process_runner.cpp" />
|
||||
<ClCompile Include="..\src\tools\tools.cpp" />
|
||||
<ClCompile Include="..\src\utility.cpp" />
|
||||
<ClCompile Include="..\src\utility\string.cpp" />
|
||||
<ClCompile Include="..\src\utility\threading.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -121,6 +122,8 @@
|
||||
<ClInclude Include="..\src\tasks\tasks.h" />
|
||||
<ClInclude Include="..\src\tools\tools.h" />
|
||||
<ClInclude Include="..\src\utility.h" />
|
||||
<ClInclude Include="..\src\utility\assert.h" />
|
||||
<ClInclude Include="..\src\utility\string.h" />
|
||||
<ClInclude Include="..\src\utility\threading.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
|
||||
@@ -156,6 +156,9 @@
|
||||
<ClCompile Include="..\src\utility\threading.cpp">
|
||||
<Filter>src\utility</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\utility\string.cpp">
|
||||
<Filter>src\utility</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\src\pch.h">
|
||||
@@ -197,5 +200,11 @@
|
||||
<ClInclude Include="..\src\utility\threading.h">
|
||||
<Filter>src\utility</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\utility\string.h">
|
||||
<Filter>src\utility</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\utility\assert.h">
|
||||
<Filter>src\utility</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
Reference in New Issue
Block a user