diff options
author | AnotherTest <ali.mpfard@gmail.com> | 2020-06-29 06:22:58 +0430 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-07-05 15:43:14 +0200 |
commit | b8d1edb2a2dbbe19afd7074c7d8bb729a75ccc4a (patch) | |
tree | 52a7c8248e097dc1d2495bc4583b15662716b7f5 | |
parent | d6de2b582860c3e533c4420ce6249c7f9b753e87 (diff) | |
download | serenity-b8d1edb2a2dbbe19afd7074c7d8bb729a75ccc4a.zip |
Shell: Add a 'setopt' builtin
This builtin sets (and unsets) boolean flags that alter the behaviour of
the shell.
The only flags added are
- inline_exec_keep_empty_segments: Keep empty segments in the result of
splitting $(...) by $IFS
- verbose: Announce each command before executing it
It should be noted that the (rather extreme) verbosity of the names is
intentional, and will hopefully be alleviated by the next commit :^)
-rw-r--r-- | Shell/AST.cpp | 4 | ||||
-rw-r--r-- | Shell/AST.h | 4 | ||||
-rw-r--r-- | Shell/Builtin.cpp | 39 | ||||
-rw-r--r-- | Shell/Shell.cpp | 8 | ||||
-rw-r--r-- | Shell/Shell.h | 14 |
5 files changed, 66 insertions, 3 deletions
diff --git a/Shell/AST.cpp b/Shell/AST.cpp index cbb17463a7..10766edfae 100644 --- a/Shell/AST.cpp +++ b/Shell/AST.cpp @@ -818,7 +818,7 @@ RefPtr<Value> Execute::run(RefPtr<Shell> shell) dbg() << "close() failed: " << strerror(errno); } - return create<StringValue>(builder.build(), shell->local_variable_or("IFS", "\n")); + return create<StringValue>(builder.build(), shell->local_variable_or("IFS", "\n"), shell->options.inline_exec_keep_empty_segments); } run_commands(commands); @@ -1799,7 +1799,7 @@ StringValue::~StringValue() Vector<String> StringValue::resolve_as_list(RefPtr<Shell>) { if (is_list()) { - auto parts = StringView(m_string).split_view(m_split); + auto parts = StringView(m_string).split_view(m_split, m_keep_empty); Vector<String> result; result.ensure_capacity(parts.size()); for (auto& part : parts) diff --git a/Shell/AST.h b/Shell/AST.h index 9426bd02ef..1c01200007 100644 --- a/Shell/AST.h +++ b/Shell/AST.h @@ -238,15 +238,17 @@ public: virtual ~StringValue(); virtual bool is_string() const override { return m_split.is_null(); } virtual bool is_list() const override { return !m_split.is_null(); } - StringValue(String string, String split_by = {}) + StringValue(String string, String split_by = {}, bool keep_empty = false) : m_string(string) , m_split(move(split_by)) + , m_keep_empty(keep_empty) { } private: String m_string; String m_split; + bool m_keep_empty { false }; }; class GlobValue final : public Value { diff --git a/Shell/Builtin.cpp b/Shell/Builtin.cpp index a9526eb641..ed56065190 100644 --- a/Shell/Builtin.cpp +++ b/Shell/Builtin.cpp @@ -645,6 +645,45 @@ int Shell::builtin_pwd(int, const char**) return 0; } +int Shell::builtin_setopt(int argc, const char** argv) +{ + if (argc == 1) { +#define __ENUMERATE_SHELL_OPTION(name, default_, description) \ + if (options.name) \ + fprintf(stderr, #name "\n"); + + ENUMERATE_SHELL_OPTIONS(); + +#undef __ENUMERATE_SHELL_OPTION + } + + Core::ArgsParser parser; +#define __ENUMERATE_SHELL_OPTION(name, default_, description) \ + bool name = false; \ + bool not_##name = false; \ + parser.add_option(name, "Enable: " description, #name, '\0'); \ + parser.add_option(not_##name, "Disable: " description, "no_" #name, '\0'); + + ENUMERATE_SHELL_OPTIONS(); + +#undef __ENUMERATE_SHELL_OPTION + + if (!parser.parse(argc, const_cast<char**>(argv), false)) + return 1; + +#define __ENUMERATE_SHELL_OPTION(name, default_, description) \ + if (name) \ + options.name = true; \ + if (not_##name) \ + options.name = false; + + ENUMERATE_SHELL_OPTIONS(); + +#undef __ENUMERATE_SHELL_OPTION + + return 0; +} + int Shell::builtin_time(int argc, const char** argv) { Vector<const char*> args; diff --git a/Shell/Shell.cpp b/Shell/Shell.cpp index b04d134774..c268e70606 100644 --- a/Shell/Shell.cpp +++ b/Shell/Shell.cpp @@ -356,6 +356,14 @@ RefPtr<Job> Shell::run_command(AST::Command& command) { FileDescriptionCollector fds; + if (options.verbose) { + fprintf(stderr, "+ "); + for (auto& arg : command.argv) + fprintf(stderr, "%s ", escape_token(arg).characters()); + fprintf(stderr, "\n"); + fflush(stderr); + } + // Resolve redirections. NonnullRefPtrVector<AST::Rewiring> rewirings; for (auto& redirection : command.redirections) { diff --git a/Shell/Shell.h b/Shell/Shell.h index 18b1c275a7..4b82d6a9f4 100644 --- a/Shell/Shell.h +++ b/Shell/Shell.h @@ -52,12 +52,17 @@ __ENUMERATE_SHELL_BUILTIN(dirs) \ __ENUMERATE_SHELL_BUILTIN(pushd) \ __ENUMERATE_SHELL_BUILTIN(popd) \ + __ENUMERATE_SHELL_BUILTIN(setopt) \ __ENUMERATE_SHELL_BUILTIN(time) \ __ENUMERATE_SHELL_BUILTIN(jobs) \ __ENUMERATE_SHELL_BUILTIN(disown) \ __ENUMERATE_SHELL_BUILTIN(fg) \ __ENUMERATE_SHELL_BUILTIN(bg) +#define ENUMERATE_SHELL_OPTIONS() \ + __ENUMERATE_SHELL_OPTION(inline_exec_keep_empty_segments, false, "Keep empty segments in inline execute $(...)") \ + __ENUMERATE_SHELL_OPTION(verbose, false, "Announce every command that is about to be executed") + class Shell; class Shell : public Core::Object { @@ -135,6 +140,15 @@ public: ReadLine, }; +#define __ENUMERATE_SHELL_OPTION(name, default_, description) \ + bool name { default_ }; + + struct Options { + ENUMERATE_SHELL_OPTIONS(); + } options; + +#undef __ENUMERATE_SHELL_OPTION + private: Shell(); virtual ~Shell() override; |