aboutsummaryrefslogtreecommitdiff
path: root/src/libutil/shlex.cc
diff options
context:
space:
mode:
authorRebecca Turner <rbt@sent.as>2024-03-14 17:44:43 -0700
committerRebecca Turner <rbt@sent.as>2024-03-26 16:44:04 -0700
commitaee3d639b5096349413021537ae842c8c33ef6cf (patch)
treeee8557f970f5c477116bd1b77a8b9cacce59d03a /src/libutil/shlex.cc
parentda22dbc33397c9c6c5d115ce753d5cf11585291e (diff)
Move `shell_words` into its own file
Change-Id: I34c0ebfb6dcea49bf632d8880e04075335a132bf
Diffstat (limited to 'src/libutil/shlex.cc')
-rw-r--r--src/libutil/shlex.cc77
1 files changed, 77 insertions, 0 deletions
diff --git a/src/libutil/shlex.cc b/src/libutil/shlex.cc
new file mode 100644
index 000000000..b5f340251
--- /dev/null
+++ b/src/libutil/shlex.cc
@@ -0,0 +1,77 @@
+#include "shlex.hh"
+#include "util.hh"
+
+namespace nix {
+
+std::vector<std::string> shell_split(const std::string & input)
+{
+ std::vector<std::string> result;
+
+ // Hack: `shell_split` is janky and parses ` a` as `{"", "a"}`, so we trim
+ // whitespace before starting.
+ auto inputTrimmed = trim(input);
+
+ if (inputTrimmed.empty()) {
+ return result;
+ }
+
+ std::regex whitespace("^\\s+");
+ auto begin = inputTrimmed.cbegin();
+ std::string currentToken;
+ enum State { sBegin, sSingleQuote, sDoubleQuote };
+ State state = sBegin;
+ auto iterator = begin;
+
+ for (; iterator != inputTrimmed.cend(); ++iterator) {
+ if (state == sBegin) {
+ std::smatch match;
+ if (regex_search(iterator, inputTrimmed.cend(), match, whitespace)) {
+ currentToken.append(begin, iterator);
+ result.push_back(currentToken);
+ iterator = match[0].second;
+ if (iterator == inputTrimmed.cend()) {
+ return result;
+ }
+ begin = iterator;
+ currentToken.clear();
+ }
+ }
+
+ switch (*iterator) {
+ case '\'':
+ if (state != sDoubleQuote) {
+ currentToken.append(begin, iterator);
+ begin = iterator + 1;
+ state = state == sBegin ? sSingleQuote : sBegin;
+ }
+ break;
+
+ case '"':
+ if (state != sSingleQuote) {
+ currentToken.append(begin, iterator);
+ begin = iterator + 1;
+ state = state == sBegin ? sDoubleQuote : sBegin;
+ }
+ break;
+
+ case '\\':
+ if (state != sSingleQuote) {
+ // perl shellwords mostly just treats the next char as part
+ // of the string with no special processing
+ currentToken.append(begin, iterator);
+ begin = ++iterator;
+ }
+ break;
+ }
+ }
+
+ if (state != sBegin) {
+ throw ShlexError(input);
+ }
+
+ currentToken.append(begin, iterator);
+ result.push_back(currentToken);
+ return result;
+}
+
+}