Shell: Add support for ARGV (and $*, $#)

This patchset also adds the 'shift' builtin, as well as the usual tests.
closes #2948.
This commit is contained in:
AnotherTest 2020-08-04 09:27:25 +04:30 committed by Andreas Kling
parent 192b2383ac
commit 12af65c1c9
8 changed files with 79 additions and 1 deletions

View file

@ -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 { "" };
}

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -162,6 +162,8 @@ dquoted_string_inner :: '\' . dquoted_string_inner? {concat}
variable :: '$' identifier
| '$' '$'
| '$' '?'
| '$' '*'
| '$' '#'
| ...
comment :: '#' [^\n]*

View file

@ -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) \

View file

@ -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

View file

@ -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);