From 608addf75a6283d6db9467dc27272a9e28fe4670 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Thu, 6 Feb 2025 17:27:18 +0100 Subject: Add argsplit to support multiple arguments in CXXFLAGS, CFLAGS and LDFLAGS. --- src/ctor.h | 2 +- src/tools.cc | 10 +-- src/util.cc | 93 +++++++++++++++++++++++ src/util.h | 3 + test/argsplit_test.cc | 203 ++++++++++++++++++++++++++++++++++++++++++++++++++ test/ctor.cc | 17 +++++ 6 files changed, 322 insertions(+), 6 deletions(-) create mode 100644 test/argsplit_test.cc diff --git a/src/ctor.h b/src/ctor.h index 27706c7..6cb46a5 100644 --- a/src/ctor.h +++ b/src/ctor.h @@ -191,7 +191,7 @@ using asm_flag = ctor::flag; using c_flags = std::vector; using cxx_flags = std::vector; -using ld_flags= std::vector; +using ld_flags = std::vector; using ar_flags = std::vector; using asm_flags = std::vector; diff --git a/src/tools.cc b/src/tools.cc index f6cb437..dfabdff 100644 --- a/src/tools.cc +++ b/src/tools.cc @@ -441,7 +441,7 @@ std::vector cxx_option(ctor::cxx_opt opt, const std::string& arg, } return {"-D" + arg}; case ctor::cxx_opt::custom: - return {arg}; + return argsplit(arg); } std::cerr << "Unsupported compiler option.\n"; @@ -488,7 +488,7 @@ std::vector c_option(ctor::c_opt opt, const std::string& arg, } return {"-D" + arg}; case ctor::c_opt::custom: - return {arg}; + return argsplit(arg); } std::cerr << "Unsupported compiler option.\n"; @@ -521,7 +521,7 @@ std::vector ld_option(ctor::ld_opt opt, const std::string& arg, case ctor::ld_opt::position_independent_executable: return {"-fPIE"}; case ctor::ld_opt::custom: - return {arg}; + return argsplit(arg); } std::cerr << "Unsupported compiler option.\n"; @@ -542,7 +542,7 @@ std::vector ar_option(ctor::ar_opt opt, const std::string& arg, case ctor::ar_opt::output: return {arg}; case ctor::ar_opt::custom: - return {arg}; + return argsplit(arg); } std::cerr << "Unsupported compiler option.\n"; @@ -555,7 +555,7 @@ std::vector asm_option(ctor::asm_opt opt, const std::string& arg, switch(opt) { case ctor::asm_opt::custom: - return {arg}; + return argsplit(arg); } std::cerr << "Unsupported compiler option.\n"; diff --git a/src/util.cc b/src/util.cc index dbd4c3c..6fc650a 100644 --- a/src/util.cc +++ b/src/util.cc @@ -6,6 +6,7 @@ #include #include #include +#include std::string to_lower(const std::string& str) { @@ -184,3 +185,95 @@ std::string locate(const std::string& prog, return {}; } + +std::vector argsplit(const std::string& str) +{ + enum class state + { + normal, + in_quot, + in_apotrophe, + } state{state::normal}; + bool esc{false}; + + std::string token; + std::vector tokens; + for(auto c : str) + { + switch(state) + { + case state::normal: + if(esc) + { + esc = false; + } + else + { + if(c == ' ') + { + tokens.push_back(token); + token.clear(); + continue; + } + if(c == '\\') + { + esc = true; + } + if(c == '"') + { + state = state::in_quot; + } + if(c == '\'') + { + state = state::in_apotrophe; + } + } + + token += c; + break; + case state::in_quot: + if(esc) + { + esc = false; + } + else + { + if(c == '\\') + { + esc = true; + } + if(c == '"') + { + state = state::normal; + } + } + + token += c; + break; + case state::in_apotrophe: + if(esc) + { + esc = false; + } + else + { + if(c == '\\') + { + esc = true; + } + if(c == '\'') + { + state = state::normal; + } + } + + token += c; + break; + } + } + if(!token.empty()) + { + tokens.push_back(token); + } + return tokens; +} diff --git a/src/util.h b/src/util.h index 0a90049..8b41014 100644 --- a/src/util.h +++ b/src/util.h @@ -28,3 +28,6 @@ std::vector get_paths(const std::string& path_env = std::getenv("PA std::string locate(const std::string& app, const std::vector& paths, const std::string& arch = {}); + +//! Splits string into tokens adhering to quotations " and ' +std::vector argsplit(const std::string& str); diff --git a/test/argsplit_test.cc b/test/argsplit_test.cc new file mode 100644 index 0000000..7dce561 --- /dev/null +++ b/test/argsplit_test.cc @@ -0,0 +1,203 @@ +#include + +#include + +class ArgSplitTest + : public uUnit +{ +public: + using fs = std::filesystem::path; + + ArgSplitTest() + { + uTEST(ArgSplitTest::plain_test); + uTEST(ArgSplitTest::quot_test); + uTEST(ArgSplitTest::apotrophe_test); + uTEST(ArgSplitTest::mixed_test); + uTEST(ArgSplitTest::escape_test); + } + + void plain_test() + { + using namespace std::string_literals; + + { // zero + auto res = argsplit({}); + uASSERT_EQUAL(0u, res.size()); + } + + { // one + auto res = argsplit("hello"s); + uASSERT_EQUAL(1u, res.size()); + uASSERT_EQUAL("hello"s, res[0]); + } + + { // many + auto res = argsplit("hello world"s); + uASSERT_EQUAL(2u, res.size()); + uASSERT_EQUAL("hello"s, res[0]); + uASSERT_EQUAL("world"s, res[1]); + } + } + + void quot_test() + { + using namespace std::string_literals; + + { // zero + auto res = argsplit("\"\""); + uASSERT_EQUAL(1u, res.size()); + uASSERT_EQUAL("\"\""s, res[0]); + } + + { // one + auto res = argsplit("\"hello\""s); + uASSERT_EQUAL(1u, res.size()); + uASSERT_EQUAL("\"hello\""s, res[0]); + } + + { // one with space + auto res = argsplit("\"hel lo\""s); + uASSERT_EQUAL(1u, res.size()); + uASSERT_EQUAL("\"hel lo\""s, res[0]); + } + + { // many + auto res = argsplit("\"hello\" \"world\""s); + uASSERT_EQUAL(2u, res.size()); + uASSERT_EQUAL("\"hello\""s, res[0]); + uASSERT_EQUAL("\"world\""s, res[1]); + } + + { // many with spaces + auto res = argsplit("\"hel lo\" \"wor ld\""s); + uASSERT_EQUAL(2u, res.size()); + uASSERT_EQUAL("\"hel lo\""s, res[0]); + uASSERT_EQUAL("\"wor ld\""s, res[1]); + } + } + + void apotrophe_test() + { + using namespace std::string_literals; + + { // zero + auto res = argsplit("\'\'"); + uASSERT_EQUAL(1u, res.size()); + uASSERT_EQUAL("\'\'"s, res[0]); + } + + { // one + auto res = argsplit("\'hello\'"s); + uASSERT_EQUAL(1u, res.size()); + uASSERT_EQUAL("\'hello\'"s, res[0]); + } + + { // one with space + auto res = argsplit("\'hel lo\'"s); + uASSERT_EQUAL(1u, res.size()); + uASSERT_EQUAL("\'hel lo\'"s, res[0]); + } + + { // many + auto res = argsplit("\'hello\' \'world\'"s); + uASSERT_EQUAL(2u, res.size()); + uASSERT_EQUAL("\'hello\'"s, res[0]); + uASSERT_EQUAL("\'world\'"s, res[1]); + } + + { // many with spaces + auto res = argsplit("\'hel lo\' \'wor ld\'"s); + uASSERT_EQUAL(2u, res.size()); + uASSERT_EQUAL("\'hel lo\'"s, res[0]); + uASSERT_EQUAL("\'wor ld\'"s, res[1]); + } + } + + void mixed_test() + { + using namespace std::string_literals; + + { // zero + auto res = argsplit("\'\'"); + uASSERT_EQUAL(1u, res.size()); + uASSERT_EQUAL("\'\'"s, res[0]); + } + + { // one + auto res = argsplit("\'he\"llo\'"s); + uASSERT_EQUAL(1u, res.size()); + uASSERT_EQUAL("\'he\"llo\'"s, res[0]); + } + + { // one + auto res = argsplit("\"he\'llo\""s); + uASSERT_EQUAL(1u, res.size()); + uASSERT_EQUAL("\"he\'llo\""s, res[0]); + } + + { // one with space + auto res = argsplit("\'he\"l lo\'\""s); + uASSERT_EQUAL(1u, res.size()); + uASSERT_EQUAL("\'he\"l lo\'\""s, res[0]); + } + + { // one with space + auto res = argsplit("\"he\'l lo\"\'"s); + uASSERT_EQUAL(1u, res.size()); + uASSERT_EQUAL("\"he\'l lo\"\'"s, res[0]); + } + + { // many + auto res = argsplit("\"he\'llo\" \"wor\'ld\""s); + uASSERT_EQUAL(2u, res.size()); + uASSERT_EQUAL("\"he\'llo\""s, res[0]); + uASSERT_EQUAL("\"wor\'ld\""s, res[1]); + } + + { // many with spaces + auto res = argsplit("\'hel\"lo\' \'wor\"ld\'"s); + uASSERT_EQUAL(2u, res.size()); + uASSERT_EQUAL("\'hel\"lo\'"s, res[0]); + uASSERT_EQUAL("\'wor\"ld\'"s, res[1]); + } + } + + void escape_test() + { + using namespace std::string_literals; + + { // zero + auto res = argsplit("\\"); + uASSERT_EQUAL(1u, res.size()); + uASSERT_EQUAL("\\"s, res[0]); + } + + { // one + auto res = argsplit("\\\'"s); + uASSERT_EQUAL(1u, res.size()); + uASSERT_EQUAL("\\\'"s, res[0]); + } + + { // one + auto res = argsplit("\\\""s); + uASSERT_EQUAL(1u, res.size()); + uASSERT_EQUAL("\\\""s, res[0]); + } + + { // one + auto res = argsplit("\\\\"s); + uASSERT_EQUAL(1u, res.size()); + uASSERT_EQUAL("\\\\"s, res[0]); + } + + { // one + auto res = argsplit("a\\ b"s); + uASSERT_EQUAL(1u, res.size()); + uASSERT_EQUAL("a\\ b"s, res[0]); + } + } +}; + +// Registers the fixture into the 'registry' +static ArgSplitTest test; diff --git a/test/ctor.cc b/test/ctor.cc index 31c63ab..b7bcc6d 100644 --- a/test/ctor.cc +++ b/test/ctor.cc @@ -9,6 +9,23 @@ ctor::build_configurations ctorTestConfigs(const ctor::settings& settings) { return { + { + .type = ctor::target_type::unit_test, + .system = ctor::output_system::build, + .target = "argsplit_test", + .sources = { + "argsplit_test.cc", + "testmain.cc", + "../src/util.cc", + }, + .flags = { + .cxxflags = { + "-std=c++20", "-O3", "-Wall", "-Werror", + "-I../src", "-Iuunit", + "-DOUTPUT=\"argsplit\"", + }, + }, + }, { .type = ctor::target_type::unit_test, .system = ctor::output_system::build, -- cgit v1.2.3