summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBent Bisballe Nyeng <deva@aasimon.org>2025-02-06 17:27:18 +0100
committerBent Bisballe Nyeng <deva@aasimon.org>2025-02-06 17:27:18 +0100
commit608addf75a6283d6db9467dc27272a9e28fe4670 (patch)
tree68d63c29ede9d4eec64f3a0c8a2a7fdc651af738
parentf3fedaf53ed90cb5f1eda704c6f199a8b34ecc96 (diff)
Add argsplit to support multiple arguments in CXXFLAGS, CFLAGS and LDFLAGS.
-rw-r--r--src/ctor.h2
-rw-r--r--src/tools.cc10
-rw-r--r--src/util.cc93
-rw-r--r--src/util.h3
-rw-r--r--test/argsplit_test.cc203
-rw-r--r--test/ctor.cc17
6 files changed, 322 insertions, 6 deletions
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<ctor::asm_opt>;
using c_flags = std::vector<ctor::c_flag>;
using cxx_flags = std::vector<ctor::cxx_flag>;
-using ld_flags= std::vector<ctor::ld_flag>;
+using ld_flags = std::vector<ctor::ld_flag>;
using ar_flags = std::vector<ctor::ar_flag>;
using asm_flags = std::vector<ctor::asm_flag>;
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<std::string> 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<std::string> 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<std::string> 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<std::string> 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<std::string> 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 <iostream>
#include <fstream>
#include <algorithm>
+#include <sstream>
std::string to_lower(const std::string& str)
{
@@ -184,3 +185,95 @@ std::string locate(const std::string& prog,
return {};
}
+
+std::vector<std::string> argsplit(const std::string& str)
+{
+ enum class state
+ {
+ normal,
+ in_quot,
+ in_apotrophe,
+ } state{state::normal};
+ bool esc{false};
+
+ std::string token;
+ std::vector<std::string> 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<std::string> get_paths(const std::string& path_env = std::getenv("PA
std::string locate(const std::string& app,
const std::vector<std::string>& paths,
const std::string& arch = {});
+
+//! Splits string into tokens adhering to quotations " and '
+std::vector<std::string> 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 <uunit.h>
+
+#include <util.h>
+
+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
@@ -12,6 +12,23 @@ ctor::build_configurations ctorTestConfigs(const ctor::settings& settings)
{
.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,
.target = "pointerlist_test",
.sources = {
"pointerlist_test.cc",