summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnotherTest <ali.mpfard@gmail.com>2020-06-29 06:22:58 +0430
committerAndreas Kling <kling@serenityos.org>2020-07-05 15:43:14 +0200
commitb8d1edb2a2dbbe19afd7074c7d8bb729a75ccc4a (patch)
tree52a7c8248e097dc1d2495bc4583b15662716b7f5
parentd6de2b582860c3e533c4420ce6249c7f9b753e87 (diff)
downloadserenity-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.cpp4
-rw-r--r--Shell/AST.h4
-rw-r--r--Shell/Builtin.cpp39
-rw-r--r--Shell/Shell.cpp8
-rw-r--r--Shell/Shell.h14
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;