diff --git a/Applications/Terminal/main.cpp b/Applications/Terminal/main.cpp index 3afb370a89..babf0b52bf 100644 --- a/Applications/Terminal/main.cpp +++ b/Applications/Terminal/main.cpp @@ -65,9 +65,9 @@ static void make_shell(int ptm_fd) perror("ioctl(TIOCSCTTY)"); exit(1); } - char* args[] = { "/bin/sh", nullptr }; + char* args[] = { "/bin/Shell", nullptr }; char* envs[] = { "TERM=xterm", "PATH=/bin:/usr/bin", nullptr }; - rc = execve("/bin/sh", args, envs); + rc = execve("/bin/Shell", args, envs); if (rc < 0) { perror("execve"); exit(1); diff --git a/Kernel/sync.sh b/Kernel/sync.sh index f6267021ad..0592aec056 100755 --- a/Kernel/sync.sh +++ b/Kernel/sync.sh @@ -75,6 +75,8 @@ cp -v ../Games/Minesweeper/Minesweeper mnt/bin/Minesweeper ln -s Minesweeper mnt/bin/ms cp -v ../Games/Snake/Snake mnt/bin/Snake ln -s Snake mnt/bin/sn +cp -v ../Shell/Shell mnt/bin/Shell +ln -s Shell mnt/bin/sh cp -v kernel.map mnt/ # Run local sync script, if it exists diff --git a/Shell/.gitignore b/Shell/.gitignore new file mode 100644 index 0000000000..06bedaa3db --- /dev/null +++ b/Shell/.gitignore @@ -0,0 +1,3 @@ +*.o +*.d +Shell diff --git a/Shell/Makefile b/Shell/Makefile new file mode 100644 index 0000000000..535b2b75f5 --- /dev/null +++ b/Shell/Makefile @@ -0,0 +1,23 @@ +include ../Makefile.common + +OBJS = \ + Parser.o \ + main.o + +APP = Shell + +DEFINES += -DUSERLAND + +all: $(APP) + +$(APP): $(OBJS) + $(LD) -o $(APP) $(LDFLAGS) $(OBJS) -lcore -lc + +.cpp.o: + @echo "CXX $<"; $(CXX) $(CXXFLAGS) -o $@ -c $< + +-include $(OBJS:%.o=%.d) + +clean: + @echo "CLEAN"; rm -f $(APPS) $(OBJS) *.d + diff --git a/Shell/Parser.cpp b/Shell/Parser.cpp new file mode 100644 index 0000000000..5adc36cd7b --- /dev/null +++ b/Shell/Parser.cpp @@ -0,0 +1,140 @@ +#include "Parser.h" +#include +#include + +void Parser::commit_token() +{ + if (m_token.is_empty()) + return; + if (m_state == InRedirectionPath) { + m_redirections.last().path = String::copy(m_token); + m_token.clear_with_capacity(); + return; + } + m_tokens.append(String::copy(m_token)); + m_token.clear_with_capacity(); +}; + +void Parser::commit_subcommand() +{ + if (m_tokens.is_empty()) + return; + m_subcommands.append({ move(m_tokens), move(m_redirections) }); +} + +void Parser::do_pipe() +{ + m_redirections.append({ Redirection::Pipe, STDOUT_FILENO }); + commit_subcommand(); +} + +void Parser::begin_redirect_read(int fd) +{ + m_redirections.append({ Redirection::FileRead, fd }); +} + +void Parser::begin_redirect_write(int fd) +{ + m_redirections.append({ Redirection::FileWrite, fd }); +} + +Vector Parser::parse() +{ + for (int i = 0; i < m_input.length(); ++i) { + char ch = m_input.characters()[i]; + switch (m_state) { + case State::Free: + if (ch == ' ') { + commit_token(); + break; + } + if (ch == '|') { + commit_token(); + if (m_tokens.is_empty()) { + fprintf(stderr, "Syntax error: Nothing before pipe (|)\n"); + return { }; + } + do_pipe(); + break; + } + if (ch == '>') { + commit_token(); + begin_redirect_write(STDOUT_FILENO); + m_state = State::InRedirectionPath; + break; + } + if (ch == '<') { + commit_token(); + begin_redirect_read(STDIN_FILENO); + m_state = State::InRedirectionPath; + break; + } + if (ch == '\'') { + m_state = State::InSingleQuotes; + break; + } + if (ch == '\"') { + m_state = State::InDoubleQuotes; + break; + } + m_token.append(ch); + break; + case State::InRedirectionPath: + if (ch == '<') { + commit_token(); + begin_redirect_read(STDIN_FILENO); + m_state = State::InRedirectionPath; + break; + } + if (ch == '>') { + commit_token(); + begin_redirect_read(STDOUT_FILENO); + m_state = State::InRedirectionPath; + break; + } + if (ch == '|') { + commit_token(); + if (m_tokens.is_empty()) { + fprintf(stderr, "Syntax error: Nothing before pipe (|)\n"); + return { }; + } + do_pipe(); + m_state = State::Free; + break; + } + if (ch == ' ') + break; + m_token.append(ch); + break; + case State::InSingleQuotes: + if (ch == '\'') { + commit_token(); + m_state = State::Free; + break; + } + m_token.append(ch); + break; + case State::InDoubleQuotes: + if (ch == '\"') { + commit_token(); + m_state = State::Free; + break; + } + m_token.append(ch); + break; + }; + } + commit_token(); + commit_subcommand(); + + if (!m_subcommands.is_empty()) { + for (auto& redirection : m_subcommands.last().redirections) { + if (redirection.type == Redirection::Pipe) { + fprintf(stderr, "Syntax error: Nothing after last pipe (|)\n"); + return { }; + } + } + } + + return move(m_subcommands); +} diff --git a/Shell/Parser.h b/Shell/Parser.h new file mode 100644 index 0000000000..3112cc6cb9 --- /dev/null +++ b/Shell/Parser.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include + +struct Redirection { + enum Type { Pipe, FileWrite, FileRead, Rewire }; + Type type; + int fd { -1 }; + int rewire_fd { -1 }; + String path { }; +}; + +struct Subcommand { + Vector args; + Vector redirections; +}; + +class Parser { +public: + explicit Parser(const String& input) : m_input(input) { } + + Vector parse(); + +private: + void commit_token(); + void commit_subcommand(); + void do_pipe(); + void begin_redirect_read(int fd); + void begin_redirect_write(int fd); + + enum State { + Free, + InSingleQuotes, + InDoubleQuotes, + InRedirectionPath, + }; + State m_state { Free }; + String m_input; + + Vector m_subcommands; + Vector m_tokens; + Vector m_redirections; + Vector m_token; +}; + diff --git a/Userland/sh.cpp b/Shell/main.cpp similarity index 73% rename from Userland/sh.cpp rename to Shell/main.cpp index d5d63f2277..4d0e9611b1 100644 --- a/Userland/sh.cpp +++ b/Shell/main.cpp @@ -13,6 +13,7 @@ #include #include #include +#include "Parser.h" //#define SH_DEBUG @@ -144,184 +145,6 @@ static bool handle_builtin(int argc, char** argv, int& retval) return false; } -struct Redirection { - enum Type { Pipe, FileWrite, FileRead, Rewire }; - Type type; - int fd { -1 }; - int rewire_fd { -1 }; - String path { }; -}; - -struct Subcommand { - Vector args; - Vector redirections; -}; - -class Parser { -public: - explicit Parser(const String& input) : m_input(input) { } - - Vector parse(); - -private: - void commit_token(); - void commit_subcommand(); - void do_pipe(); - void begin_redirect_read(int fd); - void begin_redirect_write(int fd); - - enum State { - Free, - InSingleQuotes, - InDoubleQuotes, - InRedirectionPath, - }; - State m_state { Free }; - String m_input; - - Vector m_subcommands; - Vector m_tokens; - Vector m_redirections; - Vector m_token; -}; - -void Parser::commit_token() -{ - if (m_token.is_empty()) - return; - if (m_state == InRedirectionPath) { - m_redirections.last().path = String::copy(m_token); - m_token.clear_with_capacity(); - return; - } - m_tokens.append(String::copy(m_token)); - m_token.clear_with_capacity(); -}; - -void Parser::commit_subcommand() -{ - if (m_tokens.is_empty()) - return; - m_subcommands.append({ move(m_tokens), move(m_redirections) }); -} - -void Parser::do_pipe() -{ - m_redirections.append({ Redirection::Pipe, STDOUT_FILENO }); - commit_subcommand(); -} - -void Parser::begin_redirect_read(int fd) -{ - m_redirections.append({ Redirection::FileRead, fd }); -} - -void Parser::begin_redirect_write(int fd) -{ - m_redirections.append({ Redirection::FileWrite, fd }); -} - -Vector Parser::parse() -{ - for (int i = 0; i < m_input.length(); ++i) { - char ch = m_input.characters()[i]; - switch (m_state) { - case State::Free: - if (ch == ' ') { - commit_token(); - break; - } - if (ch == '|') { - commit_token(); - if (m_tokens.is_empty()) { - fprintf(stderr, "Syntax error: Nothing before pipe (|)\n"); - return { }; - } - do_pipe(); - break; - } - if (ch == '>') { - commit_token(); - begin_redirect_write(STDOUT_FILENO); - m_state = State::InRedirectionPath; - break; - } - if (ch == '<') { - commit_token(); - begin_redirect_read(STDIN_FILENO); - m_state = State::InRedirectionPath; - break; - } - if (ch == '\'') { - m_state = State::InSingleQuotes; - break; - } - if (ch == '\"') { - m_state = State::InDoubleQuotes; - break; - } - m_token.append(ch); - break; - case State::InRedirectionPath: - if (ch == '<') { - commit_token(); - begin_redirect_read(STDIN_FILENO); - m_state = State::InRedirectionPath; - break; - } - if (ch == '>') { - commit_token(); - begin_redirect_read(STDOUT_FILENO); - m_state = State::InRedirectionPath; - break; - } - if (ch == '|') { - commit_token(); - if (m_tokens.is_empty()) { - fprintf(stderr, "Syntax error: Nothing before pipe (|)\n"); - return { }; - } - do_pipe(); - m_state = State::Free; - break; - } - if (ch == ' ') - break; - m_token.append(ch); - break; - case State::InSingleQuotes: - if (ch == '\'') { - commit_token(); - m_state = State::Free; - break; - } - m_token.append(ch); - break; - case State::InDoubleQuotes: - if (ch == '\"') { - commit_token(); - m_state = State::Free; - break; - } - m_token.append(ch); - break; - }; - } - commit_token(); - commit_subcommand(); - - if (!m_subcommands.is_empty()) { - for (auto& redirection : m_subcommands.last().redirections) { - if (redirection.type == Redirection::Pipe) { - fprintf(stderr, "Syntax error: Nothing after last pipe (|)\n"); - return { }; - } - } - } - - return move(m_subcommands); -} - class FileDescriptorCollector { public: FileDescriptorCollector() { }