mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 09:38:11 +00:00
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 :^)
This commit is contained in:
parent
d6de2b5828
commit
b8d1edb2a2
5 changed files with 66 additions and 3 deletions
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue