/* vim:set ts=2 sw=2 sts=2 et: */
/**
* \author Marcus Holland-Moritz (github@mhxnet.de)
* \copyright Copyright (c) Marcus Holland-Moritz
*
* This file is part of dwarfs.
*
* dwarfs is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* dwarfs is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with dwarfs. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#if __has_include()
#include
#else
#include
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef DWARFS_STACKTRACE_ENABLED
#include
#endif
#include
#include
#include
extern "C" int dwarfs_wcwidth(int ucs);
namespace dwarfs {
namespace {
inline std::string trimmed(std::string in) {
while (!in.empty() && in.back() == ' ') {
in.pop_back();
}
return in;
}
} // namespace
std::string size_with_unit(size_t size) {
return trimmed(folly::prettyPrint(size, folly::PRETTY_BYTES_IEC, true));
}
std::string time_with_unit(double sec) {
return trimmed(folly::prettyPrint(sec, folly::PRETTY_TIME_HMS, false));
}
std::string time_with_unit(std::chrono::nanoseconds ns) {
return time_with_unit(1e-9 * ns.count());
}
size_t parse_size_with_unit(std::string const& str) {
size_t value;
auto [ptr, ec]{std::from_chars(str.data(), str.data() + str.size(), value)};
if (ec != std::errc()) {
DWARFS_THROW(runtime_error, "cannot parse size value");
}
if (ptr[0] == '\0') {
return value;
}
if (ptr[1] == '\0') {
switch (ptr[0]) {
case 't':
case 'T':
value <<= 10;
[[fallthrough]];
case 'g':
case 'G':
value <<= 10;
[[fallthrough]];
case 'm':
case 'M':
value <<= 10;
[[fallthrough]];
case 'k':
case 'K':
value <<= 10;
return value;
default:
break;
}
}
DWARFS_THROW(runtime_error, "unsupported size suffix");
}
std::chrono::milliseconds parse_time_with_unit(std::string const& str) {
uint64_t value;
auto [ptr, ec]{std::from_chars(str.data(), str.data() + str.size(), value)};
if (ec != std::errc()) {
DWARFS_THROW(runtime_error, "cannot parse time value");
}
switch (ptr[0]) {
case 'h':
if (ptr[1] == '\0') {
return std::chrono::hours(value);
}
break;
case 'm':
if (ptr[1] == '\0') {
return std::chrono::minutes(value);
} else if (ptr[1] == 's' && ptr[2] == '\0') {
return std::chrono::milliseconds(value);
}
break;
case 's':
if (ptr[1] != '\0') {
break;
}
[[fallthrough]];
case '\0':
return std::chrono::seconds(value);
default:
break;
}
DWARFS_THROW(runtime_error, "unsupported time suffix");
}
std::chrono::system_clock::time_point parse_time_point(std::string const& str) {
static constexpr std::array formats{
"%Y%m%dT%H%M%S", "%Y%m%dT%H%M", "%Y%m%dT", "%F %T", "%FT%T",
"%F %R", "%FT%R", "%FT", "%F"};
for (auto const& fmt : formats) {
std::istringstream iss(str);
std::chrono::system_clock::time_point tp;
date::from_stream(iss, fmt, tp);
if (!iss.fail()) {
iss.peek();
if (iss.eof()) {
return tp;
}
}
}
DWARFS_THROW(runtime_error, "cannot parse time point");
}
size_t utf8_display_width(char const* p, size_t len) {
char const* const e = p + len;
size_t rv = 0;
while (p < e) {
auto cp = utf8::next(p, e);
rv += dwarfs_wcwidth(cp);
}
return rv;
}
size_t utf8_display_width(std::string const& str) {
return utf8_display_width(str.data(), str.size());
}
void utf8_truncate(std::string& str, size_t len) {
char const* p = str.data();
char const* const e = p + str.size();
size_t l = 0;
while (p < e && l <= len) {
auto np = p;
auto cp = utf8::next(np, e);
l += dwarfs_wcwidth(cp);
if (l > len) {
break;
}
p = np;
}
str.resize(p - str.data());
}
void utf8_sanitize(std::string& str) {
if (!utf8::is_valid(str)) [[unlikely]] {
str = utf8::replace_invalid(str);
}
}
void shorten_path_string(std::string& path, char separator, size_t max_len) {
if (utf8_display_width(path) > max_len) {
if (max_len < 3) {
path.clear();
return;
}
size_t start = 0;
while (utf8_display_width(path.data() + start, path.size() - start) >
max_len - 3) {
auto next = path.find(separator, start + 1);
if (next == std::string::npos) {
break;
}
start = next;
}
path.replace(0, start, "...");
if (utf8_display_width(path) > max_len) {
if (max_len >= 7) {
utf8_truncate(path, max_len - 3);
path += "...";
} else {
path = "...";
}
}
}
}
std::filesystem::path canonical_path(std::filesystem::path p) {
if (!p.empty()) {
try {
p = std::filesystem::canonical(p);
} catch (std::filesystem::filesystem_error const&) {
p = std::filesystem::absolute(p);
}
#ifdef _WIN32
if (auto wstr = p.wstring(); !wstr.starts_with(L"\\\\")) {
p = std::filesystem::path(L"\\\\?\\" + wstr);
}
#endif
}
return p;
}
bool getenv_is_enabled(char const* var) {
if (auto val = std::getenv(var)) {
if (auto maybeBool = try_to(val); maybeBool && *maybeBool) {
return true;
}
}
return false;
}
void setup_default_locale() {
try {
#ifdef _WIN32
char const* locale = "en_US.utf8";
#else
char const* locale = "";
#endif
std::locale::global(std::locale(locale));
if (!std::setlocale(LC_ALL, locale)) {
std::cerr << "warning: setlocale(LC_ALL, \"\") failed\n";
}
} catch (std::exception const& e) {
std::cerr << "warning: failed to set user default locale: " << e.what()
<< "\n";
try {
std::locale::global(std::locale::classic());
if (!std::setlocale(LC_ALL, "C")) {
std::cerr << "warning: setlocale(LC_ALL, \"C\") failed\n";
}
} catch (std::exception const& e) {
std::cerr << "warning: also failed to set classic locale: " << e.what()
<< "\n";
}
}
}
std::string_view basename(std::string_view path) {
auto pos = path.find_last_of("/\\");
if (pos == std::string_view::npos) {
return path;
}
return path.substr(pos + 1);
}
void ensure_binary_mode(std::ostream& os [[maybe_unused]]) {
#ifdef _WIN32
if (&os == &std::cout) {
_setmode(_fileno(stdout), _O_BINARY);
} else if (&os == &std::cerr) {
_setmode(_fileno(stderr), _O_BINARY);
}
#endif
}
std::string exception_str(std::exception const& e) {
return folly::exceptionStr(e).toStdString();
}
std::string exception_str(std::exception_ptr const& e) {
return folly::exceptionStr(e).toStdString();
}
unsigned int hardware_concurrency() noexcept {
return folly::hardware_concurrency();
}
int get_current_umask() {
// I'm pretty certain these warnings by Flawfinder are false positives.
// After all, we're just doing a no-op by re-setting the original value
// in order to read it.
auto mask = ::umask(0077); /* Flawfinder: ignore */
::umask(mask); /* Flawfinder: ignore */
return mask;
}
void install_signal_handlers() {
#ifdef DWARFS_STACKTRACE_ENABLED
folly::symbolizer::installFatalSignalHandler();
#endif
}
} // namespace dwarfs