nix-shell: fix shebang whitespace parsing

Leading whitespace after `nix-shell` used to produce an empty argument,
while an empty argument at the end of the line was ignored.

Fix the first issue by consuming the initial whitespace before calling
shellwords; fix the second issue by returning immediately if whitespace
is found at the end of the string instead of checking for an empty
string.

Also throw if quotes aren't terminated.
This commit is contained in:
Naïm Favier 2023-06-08 03:11:04 +02:00 committed by Robert Hensing
parent fa9642ec45
commit 595010b631

View file

@ -34,7 +34,7 @@ extern char * * environ __attribute__((weak));
*/ */
static std::vector<std::string> shellwords(const std::string & s) static std::vector<std::string> shellwords(const std::string & s)
{ {
std::regex whitespace("^(\\s+).*"); std::regex whitespace("^\\s+");
auto begin = s.cbegin(); auto begin = s.cbegin();
std::vector<std::string> res; std::vector<std::string> res;
std::string cur; std::string cur;
@ -51,9 +51,10 @@ static std::vector<std::string> shellwords(const std::string & s)
if (regex_search(it, s.cend(), match, whitespace)) { if (regex_search(it, s.cend(), match, whitespace)) {
cur.append(begin, it); cur.append(begin, it);
res.push_back(cur); res.push_back(cur);
cur.clear(); it = match[0].second;
it = match[1].second; if (it == s.cend()) return res;
begin = it; begin = it;
cur.clear();
} }
} }
switch (*it) { switch (*it) {
@ -80,8 +81,9 @@ static std::vector<std::string> shellwords(const std::string & s)
break; break;
} }
} }
if (st != sBegin) throw Error("unterminated quote in shebang line");
cur.append(begin, it); cur.append(begin, it);
if (!cur.empty()) res.push_back(cur); res.push_back(cur);
return res; return res;
} }
@ -140,7 +142,7 @@ static void main_nix_build(int argc, char * * argv)
for (auto line : lines) { for (auto line : lines) {
line = chomp(line); line = chomp(line);
std::smatch match; std::smatch match;
if (std::regex_match(line, match, std::regex("^#!\\s*nix-shell (.*)$"))) if (std::regex_match(line, match, std::regex("^#!\\s*nix-shell\\s+(.*)$")))
for (const auto & word : shellwords(match[1].str())) for (const auto & word : shellwords(match[1].str()))
args.push_back(word); args.push_back(word);
} }