1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 10:17:35 +00:00

sh: Refactor command parsing into a separate class.

This commit is contained in:
Andreas Kling 2019-04-25 14:33:47 +02:00
parent 07de1147ad
commit e84faadb63

View file

@ -119,35 +119,11 @@ static bool handle_builtin(int argc, char** argv, int& retval)
return false; return false;
} }
static int try_exec(const char* path, char** argv)
{
int ret = execve(path, argv, environ);
assert(ret < 0);
{
const char* search_path = "/bin";
char pathbuf[128];
sprintf(pathbuf, "%s/%s", search_path, argv[0]);
ret = execve(pathbuf, argv, environ);
assert(ret < 0);
}
{
const char* search_path = "/usr/bin";
char pathbuf[128];
sprintf(pathbuf, "%s/%s", search_path, argv[0]);
ret = execve(pathbuf, argv, environ);
}
if (ret == -1)
return -1;
return ret;
}
struct Redirection { struct Redirection {
enum Type { Pipe, File, Rewire }; enum Type { Pipe, File, Rewire };
Type type; Type type;
int fd; int fd { -1 };
int rewire_fd; int rewire_fd { -1 };
}; };
struct Subcommand { struct Subcommand {
@ -155,100 +131,127 @@ struct Subcommand {
Vector<Redirection> redirections; Vector<Redirection> redirections;
}; };
enum class ParseState { class Parser {
Free, public:
InSingleQuotes, explicit Parser(const String& input) : m_input(input) { }
InDoubleQuotes,
Vector<Subcommand> parse();
private:
void commit_token();
void commit_subcommand();
void do_pipe();
void begin_redirect_fd(int fd);
enum State {
Free,
InSingleQuotes,
InDoubleQuotes,
InRedirectionPath,
};
State m_state { Free };
String m_input;
Vector<Subcommand> m_subcommands;
Vector<String> m_tokens;
Vector<Redirection> m_redirections;
Vector<char> m_token;
}; };
static Vector<Subcommand> parse(const char* command) void Parser::commit_token()
{ {
ParseState state = ParseState::Free; if (m_token.is_empty())
Vector<Subcommand> subcommands; return;
m_tokens.append(String::copy(m_token));
m_token.clear_with_capacity();
};
Vector<String> current_command; void Parser::commit_subcommand()
Vector<char> current_token; {
if (m_tokens.is_empty())
return;
m_subcommands.append({ move(m_tokens), move(m_redirections) });
}
auto commit_token = [&] { void Parser::do_pipe()
if (current_token.is_empty()) {
return; m_redirections.append({ Redirection::Pipe, STDOUT_FILENO });
current_command.append(String::copy(current_token)); commit_subcommand();
current_token.clear_with_capacity(); }
};
auto commit_subcommand = [&] { void Parser::begin_redirect_fd(int fd)
if (current_command.is_empty()) {
return; m_redirections.append({ Redirection::File, fd });
subcommands.append({ current_command }); }
current_command.clear();
};
auto begin_pipe = [&] { Vector<Subcommand> Parser::parse()
Vector<Redirection> redirections; {
redirections.append({ Redirection::Pipe, STDOUT_FILENO }); for (int i = 0; i < m_input.length(); ++i) {
subcommands.append({ current_command, move(redirections) }); char ch = m_input.characters()[i];
current_command.clear(); switch (m_state) {
}; case State::Free:
if (ch == ' ') {
for (const char* c = command; *c; ++c) {
switch (state) {
case ParseState::Free:
if (*c == ' ') {
commit_token(); commit_token();
break; break;
} }
if (*c == '|') { if (ch == '|') {
commit_token(); commit_token();
if (current_command.is_empty()) { if (m_tokens.is_empty()) {
fprintf(stderr, "Syntax error: Nothing before pipe (|)\n"); fprintf(stderr, "Syntax error: Nothing before pipe (|)\n");
return { }; return { };
} }
begin_pipe(); do_pipe();
break; break;
} }
#if 0 if (ch == '>') {
if (*c == '>') { begin_redirect_fd(STDOUT_FILENO);
begin_redirect(STDOUT_FILENO); m_state = State::InRedirectionPath;
break; break;
} }
if (*c == '<') { if (ch == '<') {
begin_redirect(STDIN_FILENO); begin_redirect_fd(STDIN_FILENO);
m_state = State::InRedirectionPath;
break; break;
} }
#endif if (ch == '\'') {
if (*c == '\'') { m_state = State::InSingleQuotes;
state = ParseState::InSingleQuotes;
break; break;
} }
if (*c == '\"') { if (ch == '\"') {
state = ParseState::InDoubleQuotes; m_state = State::InDoubleQuotes;
break; break;
} }
current_token.append(*c); m_token.append(ch);
break; break;
case ParseState::InSingleQuotes: case State::InRedirectionPath:
if (*c == '\'') { if (ch == ' ') {
commit_token(); commit_token();
state = ParseState::Free;
break; break;
} }
current_token.append(*c);
break; break;
case ParseState::InDoubleQuotes: case State::InSingleQuotes:
if (*c == '\"') { if (ch == '\'') {
commit_token(); commit_token();
state = ParseState::Free; m_state = State::Free;
break; break;
} }
current_token.append(*c); m_token.append(ch);
break;
case State::InDoubleQuotes:
if (ch == '\"') {
commit_token();
m_state = State::Free;
break;
}
m_token.append(ch);
break; break;
}; };
} }
commit_token(); commit_token();
commit_subcommand(); commit_subcommand();
if (!subcommands.is_empty()) { if (!m_subcommands.is_empty()) {
for (auto& redirection : subcommands.last().redirections) { for (auto& redirection : m_subcommands.last().redirections) {
if (redirection.type == Redirection::Pipe) { if (redirection.type == Redirection::Pipe) {
fprintf(stderr, "Syntax error: Nothing after last pipe (|)\n"); fprintf(stderr, "Syntax error: Nothing after last pipe (|)\n");
return { }; return { };
@ -256,7 +259,7 @@ static Vector<Subcommand> parse(const char* command)
} }
} }
return subcommands; return move(m_subcommands);
} }
static int runcmd(char* cmd) static int runcmd(char* cmd)
@ -266,7 +269,7 @@ static int runcmd(char* cmd)
char buf[128]; char buf[128];
memcpy(buf, cmd, 128); memcpy(buf, cmd, 128);
auto subcommands = parse(cmd); auto subcommands = Parser(cmd).parse();
#ifdef PARSE_DEBUG #ifdef PARSE_DEBUG
for (int i = 0; i < subcommands.size(); ++i) { for (int i = 0; i < subcommands.size(); ++i) {