mirror of
https://github.com/NixOS/nix
synced 2025-07-06 21:41:48 +02:00
Implement shellSplitString for proper handling of NIX_SSHOPTS with spaces and quotes
This commit is contained in:
parent
44bc4c6365
commit
366611391e
5 changed files with 248 additions and 4 deletions
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "strings-inline.hh"
|
||||
#include "os-string.hh"
|
||||
#include "error.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
@ -48,4 +49,107 @@ template std::string dropEmptyInitThenConcatStringsSep(std::string_view, const s
|
|||
template std::string dropEmptyInitThenConcatStringsSep(std::string_view, const std::set<std::string> &);
|
||||
template std::string dropEmptyInitThenConcatStringsSep(std::string_view, const std::vector<std::string> &);
|
||||
|
||||
/**
|
||||
* Shell split string: split a string into shell arguments, respecting quotes and backslashes.
|
||||
*
|
||||
* Used for NIX_SSHOPTS handling, which previously used `tokenizeString` and was broken by
|
||||
* Arguments that need to be passed to ssh with spaces in them.
|
||||
*
|
||||
* Read https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html for the
|
||||
* POSIX shell specification, which is technically what we are implementing here.
|
||||
*/
|
||||
std::list<std::string> shellSplitString(std::string_view s)
|
||||
{
|
||||
std::list<std::string> result;
|
||||
std::string current;
|
||||
bool startedCurrent = false;
|
||||
bool escaping = false;
|
||||
|
||||
auto pushCurrent = [&]() {
|
||||
if (startedCurrent) {
|
||||
result.push_back(current);
|
||||
current.clear();
|
||||
startedCurrent = false;
|
||||
}
|
||||
};
|
||||
|
||||
auto pushChar = [&](char c) {
|
||||
current.push_back(c);
|
||||
startedCurrent = true;
|
||||
};
|
||||
|
||||
auto pop = [&]() {
|
||||
auto c = s[0];
|
||||
s.remove_prefix(1);
|
||||
return c;
|
||||
};
|
||||
|
||||
auto inDoubleQuotes = [&]() {
|
||||
startedCurrent = true;
|
||||
// in double quotes, escaping with backslash is only effective for $, `, ", and backslash
|
||||
while (!s.empty()) {
|
||||
auto c = pop();
|
||||
if (escaping) {
|
||||
switch (c) {
|
||||
case '$':
|
||||
case '`':
|
||||
case '"':
|
||||
case '\\':
|
||||
pushChar(c);
|
||||
break;
|
||||
default:
|
||||
pushChar('\\');
|
||||
pushChar(c);
|
||||
break;
|
||||
}
|
||||
escaping = false;
|
||||
} else if (c == '\\') {
|
||||
escaping = true;
|
||||
} else if (c == '"') {
|
||||
return;
|
||||
} else {
|
||||
pushChar(c);
|
||||
}
|
||||
}
|
||||
if (s.empty()) {
|
||||
throw Error("unterminated double quote");
|
||||
}
|
||||
};
|
||||
|
||||
auto inSingleQuotes = [&]() {
|
||||
startedCurrent = true;
|
||||
while (!s.empty()) {
|
||||
auto c = pop();
|
||||
if (c == '\'') {
|
||||
return;
|
||||
}
|
||||
pushChar(c);
|
||||
}
|
||||
if (s.empty()) {
|
||||
throw Error("unterminated single quote");
|
||||
}
|
||||
};
|
||||
|
||||
while (!s.empty()) {
|
||||
auto c = pop();
|
||||
if (escaping) {
|
||||
pushChar(c);
|
||||
escaping = false;
|
||||
} else if (c == '\\') {
|
||||
escaping = true;
|
||||
} else if (c == ' ' || c == '\t') {
|
||||
pushCurrent();
|
||||
} else if (c == '"') {
|
||||
inDoubleQuotes();
|
||||
} else if (c == '\'') {
|
||||
inSingleQuotes();
|
||||
} else {
|
||||
pushChar(c);
|
||||
}
|
||||
}
|
||||
|
||||
pushCurrent();
|
||||
|
||||
return result;
|
||||
}
|
||||
} // namespace nix
|
||||
|
|
|
@ -71,4 +71,11 @@ extern template std::string dropEmptyInitThenConcatStringsSep(std::string_view,
|
|||
extern template std::string dropEmptyInitThenConcatStringsSep(std::string_view, const std::set<std::string> &);
|
||||
extern template std::string dropEmptyInitThenConcatStringsSep(std::string_view, const std::vector<std::string> &);
|
||||
|
||||
/**
|
||||
* Shell split string: split a string into shell arguments, respecting quotes and backslashes.
|
||||
*
|
||||
* Used for NIX_SSHOPTS handling, which previously used `tokenizeString` and was broken by
|
||||
* Arguments that need to be passed to ssh with spaces in them.
|
||||
*/
|
||||
std::list<std::string> shellSplitString(std::string_view s);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue