mirror of
https://github.com/SerenityOS/serenity.git
synced 2025-01-23 18:02:05 -05: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()
|
||||
{
|
||||
}
|
||||
|
||||
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 { "" };
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -162,6 +162,8 @@ dquoted_string_inner :: '\' . dquoted_string_inner? {concat}
|
|||
variable :: '$' identifier
|
||||
| '$' '$'
|
||||
| '$' '?'
|
||||
| '$' '*'
|
||||
| '$' '#'
|
||||
| ...
|
||||
|
||||
comment :: '#' [^\n]*
|
||||
|
|
|
@ -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) \
|
||||
|
|
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* 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);
|
||||
|
|
Loading…
Add table
Reference in a new issue