// This file Copyright © 2019-2022 Mnemosyne LLC. // It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only), // or any future license endorsed by Mnemosyne LLC. // License text can be found in the licenses/ folder. #include #include #include #include #include // evutil_ascii_strncasecmp #include "file-info.h" #include "utils.h" using namespace std::literals; static void appendSanitizedComponent(std::string& out, std::string_view in) { // remove leading spaces auto constexpr leading_test = [](unsigned char ch) { return isspace(ch); }; auto const it = std::find_if_not(std::begin(in), std::end(in), leading_test); in.remove_prefix(std::distance(std::begin(in), it)); // remove trailing spaces and '.' auto constexpr trailing_test = [](unsigned char ch) { return (isspace(ch) != 0) || ch == '.'; }; auto const rit = std::find_if_not(std::rbegin(in), std::rend(in), trailing_test); in.remove_suffix(std::distance(std::rbegin(in), rit)); // munge banned characters // https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file auto constexpr ensure_legal_char = [](auto ch) { auto constexpr Banned = std::string_view{ "<>:\"/\\|?*" }; auto const banned = Banned.find(ch) != std::string_view::npos || (unsigned char)ch < 0x20; return banned ? '_' : ch; }; auto const old_out_len = std::size(out); std::transform(std::begin(in), std::end(in), std::back_inserter(out), ensure_legal_char); // munge banned filenames // https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file auto constexpr ReservedNames = std::array{ "CON"sv, "PRN"sv, "AUX"sv, "NUL"sv, "COM1"sv, "COM2"sv, "COM3"sv, "COM4"sv, "COM5"sv, "COM6"sv, "COM7"sv, "COM8"sv, "COM9"sv, "LPT1"sv, "LPT2"sv, "LPT3"sv, "LPT4"sv, "LPT5"sv, "LPT6"sv, "LPT7"sv, "LPT8"sv, "LPT9"sv, }; for (auto const& name : ReservedNames) { size_t const name_len = std::size(name); if (evutil_ascii_strncasecmp(out.c_str() + old_out_len, std::data(name), name_len) != 0 || (out[old_out_len + name_len] != '\0' && out[old_out_len + name_len] != '.')) { continue; } out.insert(std::begin(out) + old_out_len + name_len, '_'); break; } } std::string tr_file_info::sanitizePath(std::string_view in) { auto out = std::string{}; auto segment = std::string_view{}; while (tr_strvSep(&in, &segment, '/')) { appendSanitizedComponent(out, segment); out += '/'; } if (!std::empty(out)) // remove trailing slash { out.resize(std::size(out) - 1); } return out; }