diff options
author | AnotherTest <ali.mpfard@gmail.com> | 2020-08-04 09:27:25 +0430 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-08-04 13:40:58 +0200 |
commit | 12af65c1c9e29e1958bcd1df1e1566adc74aa8f6 (patch) | |
tree | fd9141ed2b0012437b7b23b2c5f381288741d5f7 /Shell | |
parent | 192b2383ac8ac06a9428d6e12c2003ef98625f24 (diff) | |
download | serenity-12af65c1c9e29e1958bcd1df1e1566adc74aa8f6.zip |
Shell: Add support for ARGV (and $*, $#)
This patchset also adds the 'shift' builtin, as well as the usual tests.
closes #2948.
Diffstat (limited to 'Shell')
-rw-r--r-- | Shell/AST.cpp | 14 | ||||
-rw-r--r-- | Shell/AST.h | 1 | ||||
-rw-r--r-- | Shell/Builtin.cpp | 34 | ||||
-rw-r--r-- | Shell/Parser.cpp | 2 | ||||
-rw-r--r-- | Shell/Parser.h | 2 | ||||
-rw-r--r-- | Shell/Shell.h | 1 | ||||
-rw-r--r-- | Shell/Tests/special-vars.sh | 15 | ||||
-rw-r--r-- | Shell/main.cpp | 11 |
8 files changed, 79 insertions, 1 deletions
diff --git a/Shell/AST.cpp b/Shell/AST.cpp index 262c168a80..8639bdc90b 100644 --- a/Shell/AST.cpp +++ b/Shell/AST.cpp @@ -2004,6 +2004,7 @@ RefPtr<Value> SimpleVariableValue::resolve_without_cast(RefPtr<Shell> shell) SpecialVariableValue::~SpecialVariableValue() { } + Vector<String> SpecialVariableValue::resolve_as_list(RefPtr<Shell> shell) { switch (m_name) { @@ -2011,6 +2012,19 @@ Vector<String> SpecialVariableValue::resolve_as_list(RefPtr<Shell> shell) return { String::number(shell->last_return_code) }; case '$': return { String::number(getpid()) }; + case '*': + if (auto argv = shell->lookup_local_variable("ARGV")) + return argv->resolve_as_list(shell); + return {}; + case '#': + if (auto argv = shell->lookup_local_variable("ARGV")) { + if (argv->is_list()) { + auto list_argv = static_cast<AST::ListValue*>(argv.ptr()); + return { String::number(list_argv->values().size()) }; + } + return { "1" }; + } + return { "0" }; default: return { "" }; } diff --git a/Shell/AST.h b/Shell/AST.h index 3021940a11..531e560bf2 100644 --- a/Shell/AST.h +++ b/Shell/AST.h @@ -233,6 +233,7 @@ public: } const Vector<RefPtr<Value>>& values() const { return m_contained_values; } + Vector<RefPtr<Value>>& values() { return m_contained_values; } private: Vector<RefPtr<Value>> m_contained_values; diff --git a/Shell/Builtin.cpp b/Shell/Builtin.cpp index bb27dc3fd1..a113094720 100644 --- a/Shell/Builtin.cpp +++ b/Shell/Builtin.cpp @@ -678,6 +678,40 @@ int Shell::builtin_setopt(int argc, const char** argv) return 0; } +int Shell::builtin_shift(int argc, const char** argv) +{ + int count = 1; + + Core::ArgsParser parser; + parser.add_positional_argument(count, "Shift count", "count", Core::ArgsParser::Required::No); + + if (!parser.parse(argc, const_cast<char**>(argv), false)) + return 1; + + if (count < 1) + return 0; + + auto argv_ = lookup_local_variable("ARGV"); + if (!argv_) { + fprintf(stderr, "shift: ARGV is unset\n"); + return 1; + } + + if (!argv_->is_list()) + argv_ = *new AST::ListValue({ argv_ }); + + auto& values = static_cast<AST::ListValue*>(argv_.ptr())->values(); + if ((size_t)count > values.size()) { + fprintf(stderr, "shift: shift count must not be greater than %zu\n", values.size()); + return 1; + } + + for (auto i = 0; i < count; ++i) + values.take_first(); + + return 0; +} + int Shell::builtin_time(int argc, const char** argv) { Vector<const char*> args; diff --git a/Shell/Parser.cpp b/Shell/Parser.cpp index 308c415a6a..ebb10f2bd8 100644 --- a/Shell/Parser.cpp +++ b/Shell/Parser.cpp @@ -772,6 +772,8 @@ RefPtr<AST::Node> Parser::parse_variable() switch (peek()) { case '$': case '?': + case '*': + case '#': return create<AST::SpecialVariable>(consume()); // Variable Special default: break; diff --git a/Shell/Parser.h b/Shell/Parser.h index 1e56bb68a8..b1476da607 100644 --- a/Shell/Parser.h +++ b/Shell/Parser.h @@ -162,6 +162,8 @@ dquoted_string_inner :: '\' . dquoted_string_inner? {concat} variable :: '$' identifier | '$' '$' | '$' '?' + | '$' '*' + | '$' '#' | ... comment :: '#' [^\n]* diff --git a/Shell/Shell.h b/Shell/Shell.h index 6daccd932e..5f6dd91b9c 100644 --- a/Shell/Shell.h +++ b/Shell/Shell.h @@ -53,6 +53,7 @@ __ENUMERATE_SHELL_BUILTIN(pushd) \ __ENUMERATE_SHELL_BUILTIN(popd) \ __ENUMERATE_SHELL_BUILTIN(setopt) \ + __ENUMERATE_SHELL_BUILTIN(shift) \ __ENUMERATE_SHELL_BUILTIN(time) \ __ENUMERATE_SHELL_BUILTIN(jobs) \ __ENUMERATE_SHELL_BUILTIN(disown) \ diff --git a/Shell/Tests/special-vars.sh b/Shell/Tests/special-vars.sh new file mode 100644 index 0000000000..597cc346c3 --- /dev/null +++ b/Shell/Tests/special-vars.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +test "$*" = "" || echo "Fail: Argv list not empty" && exit 1 +test "$#" -eq 0 || echo "Fail: Argv list empty but count non-zero" && exit 1 +test "$ARGV" = "$*" || echo "Fail: \$ARGV not equal to \$*" && exit 1 + +ARGV=(1 2 3) +test "$#" -eq 3 || echo "Fail: Assignment to ARGV does not affect \$#" && exit 1 +test "$*" = "1 2 3" || echo "Fail: Assignment to ARGV does not affect \$*" && exit 1 + +shift +test "$*" = "2 3" || echo "Fail: 'shift' does not work correctly" && exit 1 + +shift 2 +test "$*" = "" || echo "Fail: 'shift 2' does not work correctly" && exit 1 diff --git a/Shell/main.cpp b/Shell/main.cpp index 0653a1b383..d191474d91 100644 --- a/Shell/main.cpp +++ b/Shell/main.cpp @@ -159,12 +159,14 @@ int main(int argc, char** argv) const char* command_to_run = nullptr; const char* file_to_read_from = nullptr; + Vector<const char*> script_args; bool skip_rc_files = false; Core::ArgsParser parser; parser.add_option(command_to_run, "String to read commands from", "command-string", 'c', "command-string"); - parser.add_positional_argument(file_to_read_from, "File to read commands from", "file", Core::ArgsParser::Required::No); parser.add_option(skip_rc_files, "Skip running shellrc files", "skip-shellrc", 0); + parser.add_positional_argument(file_to_read_from, "File to read commands from", "file", Core::ArgsParser::Required::No); + parser.add_positional_argument(script_args, "Extra argumets to pass to the script (via $* and co)", "argument", Core::ArgsParser::Required::No); parser.parse(argc, argv); @@ -181,6 +183,13 @@ int main(int argc, char** argv) run_rc_file(Shell::local_init_file_path); } + { + Vector<String> args; + for (auto* arg : script_args) + args.empend(arg); + shell->set_local_variable("ARGV", *new AST::ListValue(move(args))); + } + if (command_to_run) { dbgprintf("sh -c '%s'\n", command_to_run); shell->run_command(command_to_run); |