diff --git a/src/utility.cpp b/src/utility.cpp index c3e2366..91652c2 100644 --- a/src/utility.cpp +++ b/src/utility.cpp @@ -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 split(const std::string& s, const std::string& seps) -{ - std::vector 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 split_quoted(const std::string& s, const std::string& seps) -{ - std::vector v; - bool q = false; - std::string token; - - for (std::size_t i=0; i -void trim_impl(std::basic_string& s, std::basic_string_view 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 -std::basic_string trim_copy_impl( - std::basic_string_view s, std::basic_string_view what) -{ - std::basic_string 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>& 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 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(s.size()), - ws.data(), static_cast(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(written) <= s.size()); - ws.resize(static_cast(written)); - break; - } - } - - return ws; -} - -std::optional to_multibyte(UINT to, std::wstring_view ws) -{ - std::string s; - - if (ws.empty()) - return s; - - - s.resize(static_cast( - static_cast(ws.size()) * 1.5)); - - for (int t=0; t<3; ++t) - { - const int written = WideCharToMultiByte( - to, 0, ws.data(), static_cast(ws.size()), - s.data(), static_cast(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(written) <= s.size()); - s.resize(static_cast(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(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(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 diff --git a/src/utility.h b/src/utility.h index 5f227f1..7ee4ea5 100644 --- a/src/utility.h +++ b/src/utility.h @@ -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 -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 -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 -T join(const std::vector& 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 split(const std::string& s, const std::string& seps); -std::vector 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>& 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 -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 -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(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 repeat_iterator diff --git a/src/utility/assert.h b/src/utility/assert.h new file mode 100644 index 0000000..c897222 --- /dev/null +++ b/src/utility/assert.h @@ -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 +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 +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 diff --git a/src/utility/string.cpp b/src/utility/string.cpp new file mode 100644 index 0000000..f41e4f0 --- /dev/null +++ b/src/utility/string.cpp @@ -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 split(const std::string& s, const std::string& seps) +{ + std::vector 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 split_quoted(const std::string& s, const std::string& seps) +{ + std::vector v; + bool q = false; + std::string token; + + for (std::size_t i=0; i +void trim_impl(std::basic_string& s, std::basic_string_view 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 +std::basic_string trim_copy_impl( + std::basic_string_view s, std::basic_string_view what) +{ + std::basic_string 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>& 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 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(s.size()), + ws.data(), static_cast(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(written) <= s.size()); + ws.resize(static_cast(written)); + break; + } + } + + return ws; +} + +std::optional to_multibyte(UINT to, std::wstring_view ws) +{ + std::string s; + + if (ws.empty()) + return s; + + + s.resize(static_cast( + static_cast(ws.size()) * 1.5)); + + for (int t=0; t<3; ++t) + { + const int written = WideCharToMultiByte( + to, 0, ws.data(), static_cast(ws.size()), + s.data(), static_cast(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(written) <= s.size()); + s.resize(static_cast(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(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(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 diff --git a/src/utility/string.h b/src/utility/string.h new file mode 100644 index 0000000..1cdded0 --- /dev/null +++ b/src/utility/string.h @@ -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 +T join(const std::vector& 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 split(const std::string& s, const std::string& seps); +std::vector 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>& 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 +std::string path_to_utf8(T&&) = delete; + +std::string path_to_utf8(fs::path p); + + +template +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(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 diff --git a/vs/mob.vcxproj b/vs/mob.vcxproj index 6a16259..fdb0128 100644 --- a/vs/mob.vcxproj +++ b/vs/mob.vcxproj @@ -106,6 +106,7 @@ + @@ -121,6 +122,8 @@ + + diff --git a/vs/mob.vcxproj.filters b/vs/mob.vcxproj.filters index 775607c..2000ea9 100644 --- a/vs/mob.vcxproj.filters +++ b/vs/mob.vcxproj.filters @@ -156,6 +156,9 @@ src\utility + + src\utility + @@ -197,5 +200,11 @@ src\utility + + src\utility + + + src\utility + \ No newline at end of file