Browse Source

Fix handling of non-ASCII paths and arguments

Also, run all tests in a path with cyrillic chars and spaces
default_compile_flags
vector-of-bool 4 years ago
parent
commit
bf4d020203
6 changed files with 79 additions and 8 deletions
  1. +48
    -2
      src/dds.main.cpp
  2. +11
    -0
      src/dds/catalog/catalog.test.cpp
  3. +17
    -4
      src/dds/proc.win.cpp
  4. +1
    -1
      src/dds/util/flock.win.cpp
  5. +1
    -0
      src/dds/util/glob.test.cpp
  6. +1
    -1
      tests/dds.py

+ 48
- 2
src/dds.main.cpp View File

#include <dds/util/paths.hpp> #include <dds/util/paths.hpp>
#include <dds/util/signal.hpp> #include <dds/util/signal.hpp>


#include <neo/assert.hpp>
#include <range/v3/action/join.hpp> #include <range/v3/action/join.hpp>
#include <range/v3/range/conversion.hpp> #include <range/v3/range/conversion.hpp>
#include <range/v3/view/concat.hpp> #include <range/v3/view/concat.hpp>


#include <filesystem> #include <filesystem>
#include <iostream> #include <iostream>
#include <locale.h>
#include <sstream> #include <sstream>


namespace { namespace {
## ## ## ## #### ## ## ## ## ## ## #### ## ##
*/ */


int main(int argc, char** argv) {
int main_fn(const std::vector<std::string>& argv) {
spdlog::set_pattern("[%H:%M:%S] [%^%-5l%$] %v"); spdlog::set_pattern("[%H:%M:%S] [%^%-5l%$] %v");
args::ArgumentParser parser("DDS - The drop-dead-simple library manager"); args::ArgumentParser parser("DDS - The drop-dead-simple library manager");


cli_build_deps build_deps{cli}; cli_build_deps build_deps{cli};


try { try {
parser.ParseCLI(argc, argv);
parser.ParseCLI(argv);
} catch (const args::Help&) { } catch (const args::Help&) {
std::cout << parser; std::cout << parser;
return 0; return 0;
return 2; return 2;
} }
} }

#if NEO_OS_IS_WINDOWS
std::string wstr_to_u8str(std::wstring_view in) {
if (in.empty()) {
return "";
}
auto req_size = ::WideCharToMultiByte(CP_UTF8,
0,
in.data(),
static_cast<int>(in.size()),
nullptr,
0,
nullptr,
nullptr);
neo_assert(invariant,
req_size > 0,
"Failed to convert given unicode string for main() argv",
req_size,
std::system_category().message(::GetLastError()),
::GetLastError());
std::string ret;
ret.resize(req_size);
::WideCharToMultiByte(CP_UTF8,
0,
in.data(),
static_cast<int>(in.size()),
ret.data(),
static_cast<int>(ret.size()),
nullptr,
nullptr);
return ret;
}

int wmain(int argc, wchar_t** argv) {
std::vector<std::string> u8_argv;
::setlocale(LC_ALL, ".utf8");
for (int i = 1; i < argc; ++i) {
u8_argv.emplace_back(wstr_to_u8str(argv[i]));
}
return main_fn(u8_argv);
}
#else
int main(int argc, char** argv) { return main_fn({argv + 1, argv + argc}); }
#endif

+ 11
- 0
src/dds/catalog/catalog.test.cpp View File

auto repo = dds::catalog::open(":memory:"s); auto repo = dds::catalog::open(":memory:"s);
} }


TEST_CASE("Open a catalog in a non-ascii path") {
::setlocale(LC_ALL, ".utf8");
auto THIS_DIR = dds::fs::canonical(__FILE__).parent_path();
auto BUILD_DIR
= (THIS_DIR.parent_path().parent_path().parent_path() / "_build").lexically_normal();
auto subdir = BUILD_DIR / "Ю́рий Алексе́евич Гага́рин";
dds::fs::remove_all(subdir);
dds::catalog::open(subdir / "test.db");
dds::fs::remove_all(subdir);
}

class catalog_test_case { class catalog_test_case {
public: public:
dds::catalog db = dds::catalog::open(":memory:"s); dds::catalog db = dds::catalog::open(":memory:"s);

+ 17
- 4
src/dds/proc.win.cpp View File

throw std::system_error(std::error_code(::GetLastError(), std::system_category()), what); throw std::system_error(std::error_code(::GetLastError(), std::system_category()), what);
} }


std::wstring widen(std::string_view s) {
if (s.empty()) {
return L"";
}
auto req_chars
= ::MultiByteToWideChar(CP_UTF8, 0, s.data(), static_cast<int>(s.size()), nullptr, 0);
std::wstring ret;
ret.resize(req_chars);
::MultiByteToWideChar(CP_UTF8, 0, s.data(), static_cast<int>(s.size()), ret.data(), req_chars);
return ret;
}

} // namespace } // namespace


proc_result dds::run_proc(const proc_options& opts) { proc_result dds::run_proc(const proc_options& opts) {
auto cmd_str = quote_command(opts.command);
auto cmd_str = quote_command(opts.command);
auto cmd_wide = widen(cmd_str);
dds_log(debug, "Spawning subprocess: {}", cmd_str); dds_log(debug, "Spawning subprocess: {}", cmd_str);


::SECURITY_ATTRIBUTES security = {}; ::SECURITY_ATTRIBUTES security = {};


wil::unique_process_information proc_info; wil::unique_process_information proc_info;


::STARTUPINFOA startup_info = {};
::STARTUPINFOW startup_info = {};
::RtlSecureZeroMemory(&startup_info, sizeof startup_info); ::RtlSecureZeroMemory(&startup_info, sizeof startup_info);
startup_info.hStdOutput = startup_info.hStdError = writer.get(); startup_info.hStdOutput = startup_info.hStdError = writer.get();
startup_info.dwFlags = STARTF_USESTDHANDLES; startup_info.dwFlags = STARTF_USESTDHANDLES;
startup_info.cb = sizeof startup_info; startup_info.cb = sizeof startup_info;
// DO IT! // DO IT!
okay = ::CreateProcessA(nullptr, // cmd[0].data(),
cmd_str.data(),
okay = ::CreateProcessW(nullptr, // cmd[0].data(),
cmd_wide.data(),
nullptr, nullptr,
nullptr, nullptr,
true, true,

+ 1
- 1
src/dds/util/flock.win.cpp View File



shared_file_mutex::shared_file_mutex(path_ref filepath) shared_file_mutex::shared_file_mutex(path_ref filepath)
: _path{filepath} { : _path{filepath} {
auto h = ::CreateFileA(_path.string().c_str(),
auto h = ::CreateFileW(_path.native().c_str(),
GENERIC_READ | GENERIC_WRITE, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr, nullptr,

+ 1
- 0
src/dds/util/glob.test.cpp View File

TEST_CASE("Simple glob") { TEST_CASE("Simple glob") {
auto this_dir = dds::fs::path(__FILE__).parent_path(); auto this_dir = dds::fs::path(__FILE__).parent_path();
auto glob = dds::glob::compile("*.test.cpp"); auto glob = dds::glob::compile("*.test.cpp");
::setlocale(LC_ALL, ".utf8");


auto it = glob.scan_from(this_dir); auto it = glob.scan_from(this_dir);
for (; it != glob.end(); ++it) { for (; it != glob.end(); ++it) {

+ 1
- 1
tests/dds.py View File

self.dds_exe = dds_exe self.dds_exe = dds_exe
self.test_dir = test_dir self.test_dir = test_dir
self.source_root = project_dir self.source_root = project_dir
self.scratch_dir = project_dir / '_test_scratch'
self.scratch_dir = project_dir / '_test_scratch/Ю́рий Алексе́евич Гага́рин'
self.scope = scope self.scope = scope
self.scope.callback(self.cleanup) self.scope.callback(self.cleanup)



Loading…
Cancel
Save