From 182974d61a41ee82a0559d454a2c2d7858f443a3 Mon Sep 17 00:00:00 2001 From: killua Date: Mon, 16 Mar 2026 17:41:45 +0400 Subject: [PATCH] fix: windows parsing --- src/headers/argparse | 12 +++ src/headers/parser/argument_parser.hpp | 4 +- src/headers/parser/parser_v2.hpp | 8 +- src/main.cpp | 17 +++- src/source/parser/argument_parser.cpp | 1 - .../platform_parsers/windows_parser.cpp | 84 ++++++++++++++----- 6 files changed, 95 insertions(+), 31 deletions(-) diff --git a/src/headers/argparse b/src/headers/argparse index 12059b2..4396e66 100644 --- a/src/headers/argparse +++ b/src/headers/argparse @@ -8,16 +8,28 @@ namespace argument_parser { using parser = linux_parser; } + +namespace argument_parser::v2 { + using parser = linux_parser; +} #elif __APPLE__ #include namespace argument_parser { using parser = macos_parser; } + +namespace argument_parser::v2 { + using parser = macos_parser; +} #elif _WIN32 #include namespace argument_parser { using parser = windows_parser; } + +namespace argument_parser::v2 { + using parser = windows_parser; +} #else #error "Unsupported platform" #endif diff --git a/src/headers/parser/argument_parser.hpp b/src/headers/parser/argument_parser.hpp index e752aed..2061cc4 100644 --- a/src/headers/parser/argument_parser.hpp +++ b/src/headers/parser/argument_parser.hpp @@ -4,7 +4,6 @@ #include #ifndef ARGUMENT_PARSER_HPP #define ARGUMENT_PARSER_HPP - #include #include #include @@ -158,8 +157,9 @@ namespace argument_parser { auto id = find_argument_id(arg); if (id.has_value()) { auto value = stored_arguments.find(id.value()); - if (value != stored_arguments.end() && value->second.has_value()) + if (value != stored_arguments.end() && value->second.has_value()) { return std::any_cast(value->second); + } } return std::nullopt; } diff --git a/src/headers/parser/parser_v2.hpp b/src/headers/parser/parser_v2.hpp index 3f64ca0..0b94fa5 100644 --- a/src/headers/parser/parser_v2.hpp +++ b/src/headers/parser/parser_v2.hpp @@ -38,7 +38,7 @@ namespace argument_parser::v2 { template void add_argument(std::unordered_map> const &argument_pairs) { - add_argument_impl>(argument_pairs); + add_argument_impl, T>(argument_pairs); } template void add_argument(std::initializer_list> const &pairs) { @@ -62,7 +62,7 @@ namespace argument_parser::v2 { } void add_argument(std::unordered_map const &argument_pairs) { - add_argument_impl(argument_pairs); + add_argument_impl(argument_pairs); } argument_parser::base_parser &to_v1() { @@ -91,7 +91,7 @@ namespace argument_parser::v2 { } private: - template + template void add_argument_impl(ArgsMap const &argument_pairs) { std::unordered_map found_params{ {extended_add_argument_flags::IsTyped, IsTyped}}; @@ -142,7 +142,7 @@ namespace argument_parser::v2 { required); break; case candidate_type::store_other: - base::add_argument(short_arg, long_arg, help_text, required); + base::add_argument(short_arg, long_arg, help_text, required); break; default: throw std::runtime_error("Could not match the arguments against any overload."); diff --git a/src/main.cpp b/src/main.cpp index 51722d8..bdd829c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -160,9 +160,16 @@ int v1Examples() { return 0; } +void run_store_point(argument_parser::base_parser const &parser) { + auto point = parser.get_optional("store-point"); + if (point) { + std::cout << "Point(" << point->x << ", " << point->y << ")" << std::endl; + } +} + int v2Examples() { using namespace argument_parser::v2::flags; - argument_parser::v2::base_parser parser; + argument_parser::v2::parser parser; parser.add_argument( {{ShortArgument, "e"}, {LongArgument, "echo"}, {Action, echo}, {HelpText, "echoes given variable"}}); @@ -196,11 +203,17 @@ int v2Examples() { }); parser.on_complete(::run_grep); + parser.on_complete(::run_store_point); parser.handle_arguments(conventions); return 0; } int main() { - return v2Examples(); + try { + return v2Examples(); + } catch (std::exception const &e) { + std::cerr << "Error: " << e.what() << std::endl; + return -1; + } } \ No newline at end of file diff --git a/src/source/parser/argument_parser.cpp b/src/source/parser/argument_parser.cpp index c4eaa73..b6e471b 100644 --- a/src/source/parser/argument_parser.cpp +++ b/src/source/parser/argument_parser.cpp @@ -115,7 +115,6 @@ namespace argument_parser { try { argument &corresponding_argument = get_argument(extracted); - if (corresponding_argument.expects_parameter()) { if (convention_type->requires_next_token() && (it + 1) == parsed_arguments.end()) { throw std::runtime_error("expected value for argument " + extracted.second); diff --git a/src/source/parser/platform_parsers/windows_parser.cpp b/src/source/parser/platform_parsers/windows_parser.cpp index 2ea087c..fe865e9 100644 --- a/src/source/parser/platform_parsers/windows_parser.cpp +++ b/src/source/parser/platform_parsers/windows_parser.cpp @@ -3,26 +3,13 @@ #include "windows_parser.hpp" #include +#include #include #include +#include #include -std::string utf8_from_wstring(const std::wstring &w) { - if (w.empty()) - return {}; - int needed = ::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, w.c_str(), -1, nullptr, 0, nullptr, nullptr); - if (needed <= 0) { - throw std::runtime_error("WideCharToMultiByte sizing failed (" + std::to_string(::GetLastError()) + ")"); - } - std::string out; - out.resize(static_cast(needed - 1)); - int written = - ::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, w.c_str(), -1, out.data(), needed - 1, nullptr, nullptr); - if (written <= 0) { - throw std::runtime_error("WideCharToMultiByte convert failed (" + std::to_string(::GetLastError()) + ")"); - } - return out; -} +using namespace std::string_literals; struct local_free_deleter { void operator()(void *ptr) const { @@ -33,28 +20,81 @@ struct local_free_deleter { } }; -void parse_windows_arguments(std::vector &parsed_arguments) { +std::string windows_error_message(DWORD error_code) { + LPSTR messageBuffer = nullptr; + + size_t size = FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, + error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast(&messageBuffer), 0, nullptr); + + if (size == 0 || !messageBuffer) { + return "Unknown Error ("s + std::to_string(error_code) + ")"; + } + + std::unique_ptr smartBuffer(messageBuffer); + std::string result(smartBuffer.get(), size); + result.erase(result.find_last_not_of(" \n\r\t") + 1); + + return result; +} + +std::string utf8_from_wstring(const std::wstring &w) { + if (w.empty()) + return {}; + + int needed = ::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, w.c_str(), -1, nullptr, 0, nullptr, nullptr); + if (needed <= 0) { + throw std::runtime_error("WideCharToMultiByte sizing failed ("s + windows_error_message(::GetLastError()) + + ")"); + } + std::string out; + out.resize(needed - 1); + int written = + ::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, w.c_str(), -1, out.data(), needed, nullptr, nullptr); + if (written <= 0) { + throw std::runtime_error( + "WideCharToMultiByte convert failed, Error("s + windows_error_message(::GetLastError()) + ")" + + " Size (Needed): " + std::to_string(needed) + " Size (Written): " + std::to_string(written) + + " Size (Allocated): " + std::to_string(out.size())); + } + return out; +} + +void parse_windows_arguments(std::vector &parsed_arguments, + std::function const &setProgramName) { int argc_w; std::unique_ptr argv_w(CommandLineToArgvW(GetCommandLineW(), &argc_w)); if (argv_w == nullptr) { throw std::runtime_error("CommandLineToArgvW failed"); } - for (int i = 0; i < argc_w; i++) { - std::string arg = utf8_from_wstring(argv_w[i]); - parsed_arguments.emplace_back(arg); + if (argc_w <= 0) { + return; + } + + setProgramName(utf8_from_wstring(argv_w[0])); + + for (int i = 1; i < argc_w; i++) { + try { + std::string arg = utf8_from_wstring(argv_w[i]); + parsed_arguments.emplace_back(arg); + } catch (std::runtime_error e) { + std::cerr << "Error: " << e.what() << std::endl; + } } } namespace argument_parser { windows_parser::windows_parser() { - parse_windows_arguments(parsed_arguments); + parse_windows_arguments(parsed_arguments, + [this](std::string const &program_name) { this->program_name = program_name; }); } } // namespace argument_parser namespace argument_parser::v2 { windows_parser::windows_parser() { - parse_windows_arguments(ref_parsed_args()); + parse_windows_arguments(ref_parsed_args(), + [this](std::string const &program_name) { this->set_program_name(program_name); }); } } // namespace argument_parser::v2