mirror of
https://github.com/RGBCube/serenity
synced 2025-05-28 12:45:07 +00:00
Shell: Correctly parse quoted filenames in redirection
This commit fixes the (incorrect) behaviour of treating quotes as part of the redirection filename. Fixes #1857 :^)
This commit is contained in:
parent
fc34123a54
commit
fb63b84c78
2 changed files with 73 additions and 23 deletions
|
@ -25,15 +25,15 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "Parser.h"
|
#include "Parser.h"
|
||||||
|
#include <ctype.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <ctype.h>
|
|
||||||
|
|
||||||
void Parser::commit_token(AllowEmptyToken allow_empty)
|
void Parser::commit_token(AllowEmptyToken allow_empty)
|
||||||
{
|
{
|
||||||
if (allow_empty == AllowEmptyToken::No && m_token.is_empty())
|
if (allow_empty == AllowEmptyToken::No && m_token.is_empty())
|
||||||
return;
|
return;
|
||||||
if (m_state == InRedirectionPath) {
|
if (state() == InRedirectionPath) {
|
||||||
m_redirections.last().path = String::copy(m_token);
|
m_redirections.last().path = String::copy(m_token);
|
||||||
m_token.clear_with_capacity();
|
m_token.clear_with_capacity();
|
||||||
return;
|
return;
|
||||||
|
@ -72,11 +72,19 @@ void Parser::begin_redirect_write(int fd)
|
||||||
m_redirections.append({ Redirection::FileWrite, fd });
|
m_redirections.append({ Redirection::FileWrite, fd });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Parser::in_state(State state) const
|
||||||
|
{
|
||||||
|
for (auto s : m_state_stack)
|
||||||
|
if (s == state)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Vector<Command> Parser::parse()
|
Vector<Command> Parser::parse()
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < m_input.length(); ++i) {
|
for (size_t i = 0; i < m_input.length(); ++i) {
|
||||||
char ch = m_input.characters()[i];
|
char ch = m_input.characters()[i];
|
||||||
switch (m_state) {
|
switch (state()) {
|
||||||
case State::Free:
|
case State::Free:
|
||||||
if (ch == ' ') {
|
if (ch == ' ') {
|
||||||
commit_token();
|
commit_token();
|
||||||
|
@ -102,13 +110,13 @@ Vector<Command> Parser::parse()
|
||||||
begin_redirect_write(STDOUT_FILENO);
|
begin_redirect_write(STDOUT_FILENO);
|
||||||
|
|
||||||
// Search for another > for append.
|
// Search for another > for append.
|
||||||
m_state = State::InWriteAppendOrRedirectionPath;
|
push_state(State::InWriteAppendOrRedirectionPath);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (ch == '<') {
|
if (ch == '<') {
|
||||||
commit_token();
|
commit_token();
|
||||||
begin_redirect_read(STDIN_FILENO);
|
begin_redirect_read(STDIN_FILENO);
|
||||||
m_state = State::InRedirectionPath;
|
push_state(State::InRedirectionPath);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (ch == '\\') {
|
if (ch == '\\') {
|
||||||
|
@ -122,14 +130,14 @@ Vector<Command> Parser::parse()
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (ch == '\'') {
|
if (ch == '\'') {
|
||||||
m_state = State::InSingleQuotes;
|
push_state(State::InSingleQuotes);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (ch == '\"') {
|
if (ch == '\"') {
|
||||||
m_state = State::InDoubleQuotes;
|
push_state(State::InDoubleQuotes);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// redirection from zsh-style multi-digit fd, such as {10}>file
|
// redirection from zsh-style multi-digit fd, such as {10}>file
|
||||||
if (ch == '{') {
|
if (ch == '{') {
|
||||||
bool is_multi_fd_redirection = false;
|
bool is_multi_fd_redirection = false;
|
||||||
|
@ -162,11 +170,11 @@ Vector<Command> Parser::parse()
|
||||||
if (m_input.characters()[redir_end] == '>') {
|
if (m_input.characters()[redir_end] == '>') {
|
||||||
begin_redirect_write(fd);
|
begin_redirect_write(fd);
|
||||||
// Search for another > for append.
|
// Search for another > for append.
|
||||||
m_state = State::InWriteAppendOrRedirectionPath;
|
push_state(State::InWriteAppendOrRedirectionPath);
|
||||||
}
|
}
|
||||||
if (m_input.characters()[redir_end] == '<') {
|
if (m_input.characters()[redir_end] == '<') {
|
||||||
begin_redirect_read(fd);
|
begin_redirect_read(fd);
|
||||||
m_state = State::InRedirectionPath;
|
push_state(State::InRedirectionPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
i = redir_end;
|
i = redir_end;
|
||||||
|
@ -183,7 +191,7 @@ Vector<Command> Parser::parse()
|
||||||
++i;
|
++i;
|
||||||
|
|
||||||
// Search for another > for append.
|
// Search for another > for append.
|
||||||
m_state = State::InWriteAppendOrRedirectionPath;
|
push_state(State::InWriteAppendOrRedirectionPath);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (next_ch == '<') {
|
if (next_ch == '<') {
|
||||||
|
@ -191,7 +199,7 @@ Vector<Command> Parser::parse()
|
||||||
begin_redirect_read(ch - '0');
|
begin_redirect_read(ch - '0');
|
||||||
++i;
|
++i;
|
||||||
|
|
||||||
m_state = State::InRedirectionPath;
|
push_state(State::InRedirectionPath);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -201,26 +209,30 @@ Vector<Command> Parser::parse()
|
||||||
case State::InWriteAppendOrRedirectionPath:
|
case State::InWriteAppendOrRedirectionPath:
|
||||||
if (ch == '>') {
|
if (ch == '>') {
|
||||||
commit_token();
|
commit_token();
|
||||||
m_state = State::InRedirectionPath;
|
pop_state();
|
||||||
|
push_state(State::InRedirectionPath);
|
||||||
ASSERT(m_redirections.size());
|
ASSERT(m_redirections.size());
|
||||||
m_redirections[m_redirections.size() - 1].type = Redirection::FileWriteAppend;
|
m_redirections[m_redirections.size() - 1].type = Redirection::FileWriteAppend;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not another > means that it's probably a path.
|
// Not another > means that it's probably a path.
|
||||||
m_state = InRedirectionPath;
|
pop_state();
|
||||||
|
push_state(InRedirectionPath);
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
case State::InRedirectionPath:
|
case State::InRedirectionPath:
|
||||||
if (ch == '<') {
|
if (ch == '<') {
|
||||||
commit_token();
|
commit_token();
|
||||||
begin_redirect_read(STDIN_FILENO);
|
begin_redirect_read(STDIN_FILENO);
|
||||||
m_state = State::InRedirectionPath;
|
pop_state();
|
||||||
|
push_state(State::InRedirectionPath);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (ch == '>') {
|
if (ch == '>') {
|
||||||
commit_token();
|
commit_token();
|
||||||
begin_redirect_read(STDOUT_FILENO);
|
begin_redirect_read(STDOUT_FILENO);
|
||||||
m_state = State::InRedirectionPath;
|
pop_state();
|
||||||
|
push_state(State::InRedirectionPath);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (ch == '|') {
|
if (ch == '|') {
|
||||||
|
@ -230,7 +242,15 @@ Vector<Command> Parser::parse()
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
do_pipe();
|
do_pipe();
|
||||||
m_state = State::Free;
|
pop_state();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ch == '"') {
|
||||||
|
push_state(State::InDoubleQuotes);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ch == '\'') {
|
||||||
|
push_state(State::InSingleQuotes);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (ch == ' ')
|
if (ch == ' ')
|
||||||
|
@ -239,16 +259,18 @@ Vector<Command> Parser::parse()
|
||||||
break;
|
break;
|
||||||
case State::InSingleQuotes:
|
case State::InSingleQuotes:
|
||||||
if (ch == '\'') {
|
if (ch == '\'') {
|
||||||
commit_token(AllowEmptyToken::Yes);
|
if (!in_state(State::InRedirectionPath))
|
||||||
m_state = State::Free;
|
commit_token(AllowEmptyToken::Yes);
|
||||||
|
pop_state();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
m_token.append(ch);
|
m_token.append(ch);
|
||||||
break;
|
break;
|
||||||
case State::InDoubleQuotes:
|
case State::InDoubleQuotes:
|
||||||
if (ch == '\"') {
|
if (ch == '\"') {
|
||||||
commit_token(AllowEmptyToken::Yes);
|
if (!in_state(State::InRedirectionPath))
|
||||||
m_state = State::Free;
|
commit_token(AllowEmptyToken::Yes);
|
||||||
|
pop_state();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (ch == '\\') {
|
if (ch == '\\') {
|
||||||
|
@ -270,6 +292,16 @@ Vector<Command> Parser::parse()
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while (m_state_stack.size() > 1) {
|
||||||
|
auto allow_empty = AllowEmptyToken::No;
|
||||||
|
if (state() == State::InDoubleQuotes || state() == State::InSingleQuotes)
|
||||||
|
allow_empty = AllowEmptyToken::Yes;
|
||||||
|
commit_token(allow_empty);
|
||||||
|
pop_state();
|
||||||
|
}
|
||||||
|
ASSERT(state() == State::Free);
|
||||||
|
|
||||||
commit_token();
|
commit_token();
|
||||||
commit_subcommand();
|
commit_subcommand();
|
||||||
commit_command();
|
commit_command();
|
||||||
|
|
|
@ -67,7 +67,10 @@ public:
|
||||||
Vector<Command> parse();
|
Vector<Command> parse();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum class AllowEmptyToken { No, Yes };
|
enum class AllowEmptyToken {
|
||||||
|
No,
|
||||||
|
Yes,
|
||||||
|
};
|
||||||
void commit_token(AllowEmptyToken = AllowEmptyToken::No);
|
void commit_token(AllowEmptyToken = AllowEmptyToken::No);
|
||||||
void commit_subcommand();
|
void commit_subcommand();
|
||||||
void commit_command();
|
void commit_command();
|
||||||
|
@ -82,7 +85,22 @@ private:
|
||||||
InWriteAppendOrRedirectionPath,
|
InWriteAppendOrRedirectionPath,
|
||||||
InRedirectionPath,
|
InRedirectionPath,
|
||||||
};
|
};
|
||||||
State m_state { Free };
|
|
||||||
|
State state() const { return m_state_stack.last(); }
|
||||||
|
|
||||||
|
void pop_state()
|
||||||
|
{
|
||||||
|
m_state_stack.take_last();
|
||||||
|
}
|
||||||
|
|
||||||
|
void push_state(State state)
|
||||||
|
{
|
||||||
|
m_state_stack.append(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool in_state(State) const;
|
||||||
|
|
||||||
|
Vector<State> m_state_stack { Free };
|
||||||
String m_input;
|
String m_input;
|
||||||
|
|
||||||
Vector<Command> m_commands;
|
Vector<Command> m_commands;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue