summaryrefslogtreecommitdiff
path: root/Shell
diff options
context:
space:
mode:
authorAnotherTest <ali.mpfard@gmail.com>2020-08-04 09:27:25 +0430
committerAndreas Kling <kling@serenityos.org>2020-08-04 13:40:58 +0200
commit12af65c1c9e29e1958bcd1df1e1566adc74aa8f6 (patch)
treefd9141ed2b0012437b7b23b2c5f381288741d5f7 /Shell
parent192b2383ac8ac06a9428d6e12c2003ef98625f24 (diff)
downloadserenity-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.cpp14
-rw-r--r--Shell/AST.h1
-rw-r--r--Shell/Builtin.cpp34
-rw-r--r--Shell/Parser.cpp2
-rw-r--r--Shell/Parser.h2
-rw-r--r--Shell/Shell.h1
-rw-r--r--Shell/Tests/special-vars.sh15
-rw-r--r--Shell/main.cpp11
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);