mirror of
https://github.com/RGBCube/serenity
synced 2025-05-15 10:04:59 +00:00
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:
parent
192b2383ac
commit
12af65c1c9
8 changed files with 79 additions and 1 deletions
|
@ -2004,6 +2004,7 @@ RefPtr<Value> SimpleVariableValue::resolve_without_cast(RefPtr<Shell> shell)
|
||||||
SpecialVariableValue::~SpecialVariableValue()
|
SpecialVariableValue::~SpecialVariableValue()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<String> SpecialVariableValue::resolve_as_list(RefPtr<Shell> shell)
|
Vector<String> SpecialVariableValue::resolve_as_list(RefPtr<Shell> shell)
|
||||||
{
|
{
|
||||||
switch (m_name) {
|
switch (m_name) {
|
||||||
|
@ -2011,6 +2012,19 @@ Vector<String> SpecialVariableValue::resolve_as_list(RefPtr<Shell> shell)
|
||||||
return { String::number(shell->last_return_code) };
|
return { String::number(shell->last_return_code) };
|
||||||
case '$':
|
case '$':
|
||||||
return { String::number(getpid()) };
|
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:
|
default:
|
||||||
return { "" };
|
return { "" };
|
||||||
}
|
}
|
||||||
|
|
|
@ -233,6 +233,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
const Vector<RefPtr<Value>>& values() const { return m_contained_values; }
|
const Vector<RefPtr<Value>>& values() const { return m_contained_values; }
|
||||||
|
Vector<RefPtr<Value>>& values() { return m_contained_values; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Vector<RefPtr<Value>> m_contained_values;
|
Vector<RefPtr<Value>> m_contained_values;
|
||||||
|
|
|
@ -678,6 +678,40 @@ int Shell::builtin_setopt(int argc, const char** argv)
|
||||||
return 0;
|
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)
|
int Shell::builtin_time(int argc, const char** argv)
|
||||||
{
|
{
|
||||||
Vector<const char*> args;
|
Vector<const char*> args;
|
||||||
|
|
|
@ -772,6 +772,8 @@ RefPtr<AST::Node> Parser::parse_variable()
|
||||||
switch (peek()) {
|
switch (peek()) {
|
||||||
case '$':
|
case '$':
|
||||||
case '?':
|
case '?':
|
||||||
|
case '*':
|
||||||
|
case '#':
|
||||||
return create<AST::SpecialVariable>(consume()); // Variable Special
|
return create<AST::SpecialVariable>(consume()); // Variable Special
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -162,6 +162,8 @@ dquoted_string_inner :: '\' . dquoted_string_inner? {concat}
|
||||||
variable :: '$' identifier
|
variable :: '$' identifier
|
||||||
| '$' '$'
|
| '$' '$'
|
||||||
| '$' '?'
|
| '$' '?'
|
||||||
|
| '$' '*'
|
||||||
|
| '$' '#'
|
||||||
| ...
|
| ...
|
||||||
|
|
||||||
comment :: '#' [^\n]*
|
comment :: '#' [^\n]*
|
||||||
|
|
|
@ -53,6 +53,7 @@
|
||||||
__ENUMERATE_SHELL_BUILTIN(pushd) \
|
__ENUMERATE_SHELL_BUILTIN(pushd) \
|
||||||
__ENUMERATE_SHELL_BUILTIN(popd) \
|
__ENUMERATE_SHELL_BUILTIN(popd) \
|
||||||
__ENUMERATE_SHELL_BUILTIN(setopt) \
|
__ENUMERATE_SHELL_BUILTIN(setopt) \
|
||||||
|
__ENUMERATE_SHELL_BUILTIN(shift) \
|
||||||
__ENUMERATE_SHELL_BUILTIN(time) \
|
__ENUMERATE_SHELL_BUILTIN(time) \
|
||||||
__ENUMERATE_SHELL_BUILTIN(jobs) \
|
__ENUMERATE_SHELL_BUILTIN(jobs) \
|
||||||
__ENUMERATE_SHELL_BUILTIN(disown) \
|
__ENUMERATE_SHELL_BUILTIN(disown) \
|
||||||
|
|
15
Shell/Tests/special-vars.sh
Normal file
15
Shell/Tests/special-vars.sh
Normal 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
|
|
@ -159,12 +159,14 @@ int main(int argc, char** argv)
|
||||||
|
|
||||||
const char* command_to_run = nullptr;
|
const char* command_to_run = nullptr;
|
||||||
const char* file_to_read_from = nullptr;
|
const char* file_to_read_from = nullptr;
|
||||||
|
Vector<const char*> script_args;
|
||||||
bool skip_rc_files = false;
|
bool skip_rc_files = false;
|
||||||
|
|
||||||
Core::ArgsParser parser;
|
Core::ArgsParser parser;
|
||||||
parser.add_option(command_to_run, "String to read commands from", "command-string", 'c', "command-string");
|
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_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);
|
parser.parse(argc, argv);
|
||||||
|
|
||||||
|
@ -181,6 +183,13 @@ int main(int argc, char** argv)
|
||||||
run_rc_file(Shell::local_init_file_path);
|
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) {
|
if (command_to_run) {
|
||||||
dbgprintf("sh -c '%s'\n", command_to_run);
|
dbgprintf("sh -c '%s'\n", command_to_run);
|
||||||
shell->run_command(command_to_run);
|
shell->run_command(command_to_run);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue