diff options
author | Rebecca Turner <rbt@sent.as> | 2024-03-14 17:44:43 -0700 |
---|---|---|
committer | Rebecca Turner <rbt@sent.as> | 2024-03-26 16:44:04 -0700 |
commit | aee3d639b5096349413021537ae842c8c33ef6cf (patch) | |
tree | ee8557f970f5c477116bd1b77a8b9cacce59d03a /src/libutil/shlex.cc | |
parent | da22dbc33397c9c6c5d115ce753d5cf11585291e (diff) |
Move `shell_words` into its own file
Change-Id: I34c0ebfb6dcea49bf632d8880e04075335a132bf
Diffstat (limited to 'src/libutil/shlex.cc')
-rw-r--r-- | src/libutil/shlex.cc | 77 |
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; +} + +} |