mirror of
https://github.com/RGBCube/serenity
synced 2025-05-24 06:15:07 +00:00
Shell: Convert builtins to use the modern main() style
That is, return ErrorOr<int>, handle fallible ops with TRY() and accept a Main::Arguments. Note that we do not populate the argc/argv members of Main::Arguments, so all accesses have to go through .strings.
This commit is contained in:
parent
6e5ba82929
commit
007767fc14
3 changed files with 175 additions and 148 deletions
|
@ -26,12 +26,12 @@ extern char** environ;
|
||||||
|
|
||||||
namespace Shell {
|
namespace Shell {
|
||||||
|
|
||||||
int Shell::builtin_noop(int, char const**)
|
ErrorOr<int> Shell::builtin_noop(Main::Arguments)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Shell::builtin_dump(int argc, char const** argv)
|
ErrorOr<int> Shell::builtin_dump(Main::Arguments arguments)
|
||||||
{
|
{
|
||||||
bool posix = false;
|
bool posix = false;
|
||||||
StringView source;
|
StringView source;
|
||||||
|
@ -40,10 +40,10 @@ int Shell::builtin_dump(int argc, char const** argv)
|
||||||
parser.add_positional_argument(source, "Shell code to parse and dump", "source");
|
parser.add_positional_argument(source, "Shell code to parse and dump", "source");
|
||||||
parser.add_option(posix, "Use the POSIX parser", "posix", 'p');
|
parser.add_option(posix, "Use the POSIX parser", "posix", 'p');
|
||||||
|
|
||||||
if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
|
if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
(void)(posix ? Posix::Parser { source }.parse() : Parser { source }.parse())->dump(0);
|
TRY((posix ? Posix::Parser { source }.parse() : Parser { source }.parse())->dump(0));
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,20 +80,20 @@ static Vector<DeprecatedString> find_matching_executables_in_path(StringView fil
|
||||||
return executables;
|
return executables;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Shell::builtin_where(int argc, char const** argv)
|
ErrorOr<int> Shell::builtin_where(Main::Arguments arguments)
|
||||||
{
|
{
|
||||||
Vector<StringView> arguments;
|
Vector<StringView> values_to_lookup;
|
||||||
bool do_only_path_search { false };
|
bool do_only_path_search { false };
|
||||||
bool do_follow_symlinks { false };
|
bool do_follow_symlinks { false };
|
||||||
bool do_print_only_type { false };
|
bool do_print_only_type { false };
|
||||||
|
|
||||||
Core::ArgsParser parser;
|
Core::ArgsParser parser;
|
||||||
parser.add_positional_argument(arguments, "List of shell builtins, aliases or executables", "arguments");
|
parser.add_positional_argument(values_to_lookup, "List of shell builtins, aliases or executables", "arguments");
|
||||||
parser.add_option(do_only_path_search, "Search only for executables in the PATH environment variable", "path-only", 'p');
|
parser.add_option(do_only_path_search, "Search only for executables in the PATH environment variable", "path-only", 'p');
|
||||||
parser.add_option(do_follow_symlinks, "Follow symlinks and print the symlink free path", "follow-symlink", 's');
|
parser.add_option(do_follow_symlinks, "Follow symlinks and print the symlink free path", "follow-symlink", 's');
|
||||||
parser.add_option(do_print_only_type, "Print the argument type instead of a human readable description", "type", 'w');
|
parser.add_option(do_print_only_type, "Print the argument type instead of a human readable description", "type", 'w');
|
||||||
|
|
||||||
if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
|
if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
auto const lookup_alias = [do_only_path_search, &m_aliases = this->m_aliases](StringView alias) -> Optional<DeprecatedString> {
|
auto const lookup_alias = [do_only_path_search, &m_aliases = this->m_aliases](StringView alias) -> Optional<DeprecatedString> {
|
||||||
|
@ -114,7 +114,7 @@ int Shell::builtin_where(int argc, char const** argv)
|
||||||
};
|
};
|
||||||
|
|
||||||
bool at_least_one_succeded { false };
|
bool at_least_one_succeded { false };
|
||||||
for (auto const& argument : arguments) {
|
for (auto const& argument : values_to_lookup) {
|
||||||
auto const alias = lookup_alias(argument);
|
auto const alias = lookup_alias(argument);
|
||||||
if (alias.has_value()) {
|
if (alias.has_value()) {
|
||||||
if (do_print_only_type)
|
if (do_print_only_type)
|
||||||
|
@ -147,24 +147,24 @@ int Shell::builtin_where(int argc, char const** argv)
|
||||||
return at_least_one_succeded ? 0 : 1;
|
return at_least_one_succeded ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Shell::builtin_alias(int argc, char const** argv)
|
ErrorOr<int> Shell::builtin_alias(Main::Arguments arguments)
|
||||||
{
|
{
|
||||||
Vector<DeprecatedString> arguments;
|
Vector<DeprecatedString> aliases;
|
||||||
|
|
||||||
Core::ArgsParser parser;
|
Core::ArgsParser parser;
|
||||||
parser.add_positional_argument(arguments, "List of name[=values]'s", "name[=value]", Core::ArgsParser::Required::No);
|
parser.add_positional_argument(aliases, "List of name[=values]'s", "name[=value]", Core::ArgsParser::Required::No);
|
||||||
|
|
||||||
if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
|
if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (arguments.is_empty()) {
|
if (aliases.is_empty()) {
|
||||||
for (auto& alias : m_aliases)
|
for (auto& alias : m_aliases)
|
||||||
printf("%s=%s\n", escape_token(alias.key).characters(), escape_token(alias.value).characters());
|
printf("%s=%s\n", escape_token(alias.key).characters(), escape_token(alias.value).characters());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fail = false;
|
bool fail = false;
|
||||||
for (auto& argument : arguments) {
|
for (auto& argument : aliases) {
|
||||||
auto parts = argument.split_limit('=', 2, SplitBehavior::KeepEmpty);
|
auto parts = argument.split_limit('=', 2, SplitBehavior::KeepEmpty);
|
||||||
if (parts.size() == 1) {
|
if (parts.size() == 1) {
|
||||||
auto alias = m_aliases.get(parts[0]);
|
auto alias = m_aliases.get(parts[0]);
|
||||||
|
@ -182,17 +182,17 @@ int Shell::builtin_alias(int argc, char const** argv)
|
||||||
return fail ? 1 : 0;
|
return fail ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Shell::builtin_unalias(int argc, char const** argv)
|
ErrorOr<int> Shell::builtin_unalias(Main::Arguments arguments)
|
||||||
{
|
{
|
||||||
bool remove_all { false };
|
bool remove_all { false };
|
||||||
Vector<DeprecatedString> arguments;
|
Vector<DeprecatedString> aliases;
|
||||||
|
|
||||||
Core::ArgsParser parser;
|
Core::ArgsParser parser;
|
||||||
parser.set_general_help("Remove alias from the list of aliases");
|
parser.set_general_help("Remove alias from the list of aliases");
|
||||||
parser.add_option(remove_all, "Remove all aliases", nullptr, 'a');
|
parser.add_option(remove_all, "Remove all aliases", nullptr, 'a');
|
||||||
parser.add_positional_argument(arguments, "List of aliases to remove", "alias", Core::ArgsParser::Required::No);
|
parser.add_positional_argument(aliases, "List of aliases to remove", "alias", Core::ArgsParser::Required::Yes);
|
||||||
|
|
||||||
if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
|
if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (remove_all) {
|
if (remove_all) {
|
||||||
|
@ -201,14 +201,8 @@ int Shell::builtin_unalias(int argc, char const** argv)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arguments.is_empty()) {
|
|
||||||
warnln("unalias: not enough arguments");
|
|
||||||
parser.print_usage(stderr, argv[0]);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool failed { false };
|
bool failed { false };
|
||||||
for (auto& argument : arguments) {
|
for (auto& argument : aliases) {
|
||||||
if (!m_aliases.contains(argument)) {
|
if (!m_aliases.contains(argument)) {
|
||||||
warnln("unalias: {}: alias not found", argument);
|
warnln("unalias: {}: alias not found", argument);
|
||||||
failed = true;
|
failed = true;
|
||||||
|
@ -221,7 +215,7 @@ int Shell::builtin_unalias(int argc, char const** argv)
|
||||||
return failed ? 1 : 0;
|
return failed ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Shell::builtin_bg(int argc, char const** argv)
|
ErrorOr<int> Shell::builtin_bg(Main::Arguments arguments)
|
||||||
{
|
{
|
||||||
int job_id = -1;
|
int job_id = -1;
|
||||||
bool is_pid = false;
|
bool is_pid = false;
|
||||||
|
@ -251,7 +245,7 @@ int Shell::builtin_bg(int argc, char const** argv)
|
||||||
return false;
|
return false;
|
||||||
} });
|
} });
|
||||||
|
|
||||||
if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
|
if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (job_id == -1 && !jobs.is_empty())
|
if (job_id == -1 && !jobs.is_empty())
|
||||||
|
@ -286,7 +280,7 @@ int Shell::builtin_bg(int argc, char const** argv)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Shell::builtin_type(int argc, char const** argv)
|
ErrorOr<int> Shell::builtin_type(Main::Arguments arguments)
|
||||||
{
|
{
|
||||||
Vector<DeprecatedString> commands;
|
Vector<DeprecatedString> commands;
|
||||||
bool dont_show_function_source = false;
|
bool dont_show_function_source = false;
|
||||||
|
@ -296,7 +290,7 @@ int Shell::builtin_type(int argc, char const** argv)
|
||||||
parser.add_positional_argument(commands, "Command(s) to list info about", "command");
|
parser.add_positional_argument(commands, "Command(s) to list info about", "command");
|
||||||
parser.add_option(dont_show_function_source, "Do not show functions source.", "no-fn-source", 'f');
|
parser.add_option(dont_show_function_source, "Do not show functions source.", "no-fn-source", 'f');
|
||||||
|
|
||||||
if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
|
if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
bool something_not_found = false;
|
bool something_not_found = false;
|
||||||
|
@ -355,14 +349,14 @@ int Shell::builtin_type(int argc, char const** argv)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Shell::builtin_cd(int argc, char const** argv)
|
ErrorOr<int> Shell::builtin_cd(Main::Arguments arguments)
|
||||||
{
|
{
|
||||||
char const* arg_path = nullptr;
|
char const* arg_path = nullptr;
|
||||||
|
|
||||||
Core::ArgsParser parser;
|
Core::ArgsParser parser;
|
||||||
parser.add_positional_argument(arg_path, "Path to change to", "path", Core::ArgsParser::Required::No);
|
parser.add_positional_argument(arg_path, "Path to change to", "path", Core::ArgsParser::Required::No);
|
||||||
|
|
||||||
if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
|
if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
DeprecatedString new_path;
|
DeprecatedString new_path;
|
||||||
|
@ -409,14 +403,14 @@ int Shell::builtin_cd(int argc, char const** argv)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Shell::builtin_cdh(int argc, char const** argv)
|
ErrorOr<int> Shell::builtin_cdh(Main::Arguments arguments)
|
||||||
{
|
{
|
||||||
int index = -1;
|
int index = -1;
|
||||||
|
|
||||||
Core::ArgsParser parser;
|
Core::ArgsParser parser;
|
||||||
parser.add_positional_argument(index, "Index of the cd history entry (leave out for a list)", "index", Core::ArgsParser::Required::No);
|
parser.add_positional_argument(index, "Index of the cd history entry (leave out for a list)", "index", Core::ArgsParser::Required::No);
|
||||||
|
|
||||||
if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
|
if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (index == -1) {
|
if (index == -1) {
|
||||||
|
@ -435,12 +429,12 @@ int Shell::builtin_cdh(int argc, char const** argv)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
char const* path = cd_history.at(cd_history.size() - index).characters();
|
StringView path = cd_history.at(cd_history.size() - index);
|
||||||
char const* cd_args[] = { "cd", path, nullptr };
|
StringView cd_args[] = { "cd"sv, path };
|
||||||
return Shell::builtin_cd(2, cd_args);
|
return Shell::builtin_cd({ .argc = 0, .argv = 0, .strings = cd_args });
|
||||||
}
|
}
|
||||||
|
|
||||||
int Shell::builtin_dirs(int argc, char const** argv)
|
ErrorOr<int> Shell::builtin_dirs(Main::Arguments arguments)
|
||||||
{
|
{
|
||||||
// The first directory in the stack is ALWAYS the current directory
|
// The first directory in the stack is ALWAYS the current directory
|
||||||
directory_stack.at(0) = cwd.characters();
|
directory_stack.at(0) = cwd.characters();
|
||||||
|
@ -458,7 +452,7 @@ int Shell::builtin_dirs(int argc, char const** argv)
|
||||||
parser.add_option(number_when_printing, "Number the directories in the stack when printing", "number", 'v');
|
parser.add_option(number_when_printing, "Number the directories in the stack when printing", "number", 'v');
|
||||||
parser.add_positional_argument(paths, "Extra paths to put on the stack", "path", Core::ArgsParser::Required::No);
|
parser.add_positional_argument(paths, "Extra paths to put on the stack", "path", Core::ArgsParser::Required::No);
|
||||||
|
|
||||||
if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
|
if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
// -v implies -p
|
// -v implies -p
|
||||||
|
@ -493,26 +487,24 @@ int Shell::builtin_dirs(int argc, char const** argv)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Shell::builtin_exec(int argc, char const** argv)
|
ErrorOr<int> Shell::builtin_exec(Main::Arguments arguments)
|
||||||
{
|
{
|
||||||
if (argc < 2) {
|
if (arguments.strings.size() < 2) {
|
||||||
warnln("Shell: No command given to exec");
|
warnln("Shell: No command given to exec");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<char const*> argv_vector;
|
TRY(execute_process(arguments.strings.slice(1)));
|
||||||
argv_vector.append(argv + 1, argc - 1);
|
// NOTE: Won't get here.
|
||||||
argv_vector.append(nullptr);
|
return 0;
|
||||||
|
|
||||||
execute_process(move(argv_vector));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int Shell::builtin_exit(int argc, char const** argv)
|
ErrorOr<int> Shell::builtin_exit(Main::Arguments arguments)
|
||||||
{
|
{
|
||||||
int exit_code = 0;
|
int exit_code = 0;
|
||||||
Core::ArgsParser parser;
|
Core::ArgsParser parser;
|
||||||
parser.add_positional_argument(exit_code, "Exit code", "code", Core::ArgsParser::Required::No);
|
parser.add_positional_argument(exit_code, "Exit code", "code", Core::ArgsParser::Required::No);
|
||||||
if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
|
if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (m_is_interactive) {
|
if (m_is_interactive) {
|
||||||
|
@ -532,14 +524,14 @@ int Shell::builtin_exit(int argc, char const** argv)
|
||||||
exit(exit_code);
|
exit(exit_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Shell::builtin_export(int argc, char const** argv)
|
ErrorOr<int> Shell::builtin_export(Main::Arguments arguments)
|
||||||
{
|
{
|
||||||
Vector<DeprecatedString> vars;
|
Vector<DeprecatedString> vars;
|
||||||
|
|
||||||
Core::ArgsParser parser;
|
Core::ArgsParser parser;
|
||||||
parser.add_positional_argument(vars, "List of variable[=value]'s", "values", Core::ArgsParser::Required::No);
|
parser.add_positional_argument(vars, "List of variable[=value]'s", "values", Core::ArgsParser::Required::No);
|
||||||
|
|
||||||
if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
|
if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (vars.is_empty()) {
|
if (vars.is_empty()) {
|
||||||
|
@ -554,7 +546,7 @@ int Shell::builtin_export(int argc, char const** argv)
|
||||||
if (parts.size() == 1) {
|
if (parts.size() == 1) {
|
||||||
auto value = lookup_local_variable(parts[0]);
|
auto value = lookup_local_variable(parts[0]);
|
||||||
if (value) {
|
if (value) {
|
||||||
auto values = const_cast<AST::Value&>(*value).resolve_as_list(*this).release_value_but_fixme_should_propagate_errors();
|
auto values = TRY(const_cast<AST::Value&>(*value).resolve_as_list(*this));
|
||||||
StringBuilder builder;
|
StringBuilder builder;
|
||||||
builder.join(' ', values);
|
builder.join(' ', values);
|
||||||
parts.append(builder.to_deprecated_string());
|
parts.append(builder.to_deprecated_string());
|
||||||
|
@ -578,13 +570,13 @@ int Shell::builtin_export(int argc, char const** argv)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Shell::builtin_glob(int argc, char const** argv)
|
ErrorOr<int> Shell::builtin_glob(Main::Arguments arguments)
|
||||||
{
|
{
|
||||||
Vector<DeprecatedString> globs;
|
Vector<DeprecatedString> globs;
|
||||||
Core::ArgsParser parser;
|
Core::ArgsParser parser;
|
||||||
parser.add_positional_argument(globs, "Globs to resolve", "glob");
|
parser.add_positional_argument(globs, "Globs to resolve", "glob");
|
||||||
|
|
||||||
if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
|
if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
for (auto& glob : globs) {
|
for (auto& glob : globs) {
|
||||||
|
@ -595,7 +587,7 @@ int Shell::builtin_glob(int argc, char const** argv)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Shell::builtin_fg(int argc, char const** argv)
|
ErrorOr<int> Shell::builtin_fg(Main::Arguments arguments)
|
||||||
{
|
{
|
||||||
int job_id = -1;
|
int job_id = -1;
|
||||||
bool is_pid = false;
|
bool is_pid = false;
|
||||||
|
@ -625,7 +617,7 @@ int Shell::builtin_fg(int argc, char const** argv)
|
||||||
return false;
|
return false;
|
||||||
} });
|
} });
|
||||||
|
|
||||||
if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
|
if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (job_id == -1 && !jobs.is_empty())
|
if (job_id == -1 && !jobs.is_empty())
|
||||||
|
@ -667,7 +659,7 @@ int Shell::builtin_fg(int argc, char const** argv)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Shell::builtin_disown(int argc, char const** argv)
|
ErrorOr<int> Shell::builtin_disown(Main::Arguments arguments)
|
||||||
{
|
{
|
||||||
Vector<int> job_ids;
|
Vector<int> job_ids;
|
||||||
Vector<bool> id_is_pid;
|
Vector<bool> id_is_pid;
|
||||||
|
@ -697,7 +689,7 @@ int Shell::builtin_disown(int argc, char const** argv)
|
||||||
return false;
|
return false;
|
||||||
} });
|
} });
|
||||||
|
|
||||||
if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
|
if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (job_ids.is_empty()) {
|
if (job_ids.is_empty()) {
|
||||||
|
@ -736,7 +728,7 @@ int Shell::builtin_disown(int argc, char const** argv)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Shell::builtin_history(int, char const**)
|
ErrorOr<int> Shell::builtin_history(Main::Arguments)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < m_editor->history().size(); ++i) {
|
for (size_t i = 0; i < m_editor->history().size(); ++i) {
|
||||||
printf("%6zu %s\n", i + 1, m_editor->history()[i].entry.characters());
|
printf("%6zu %s\n", i + 1, m_editor->history()[i].entry.characters());
|
||||||
|
@ -744,7 +736,7 @@ int Shell::builtin_history(int, char const**)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Shell::builtin_jobs(int argc, char const** argv)
|
ErrorOr<int> Shell::builtin_jobs(Main::Arguments arguments)
|
||||||
{
|
{
|
||||||
bool list = false, show_pid = false;
|
bool list = false, show_pid = false;
|
||||||
|
|
||||||
|
@ -752,7 +744,7 @@ int Shell::builtin_jobs(int argc, char const** argv)
|
||||||
parser.add_option(list, "List all information about jobs", "list", 'l');
|
parser.add_option(list, "List all information about jobs", "list", 'l');
|
||||||
parser.add_option(show_pid, "Display the PID of the jobs", "pid", 'p');
|
parser.add_option(show_pid, "Display the PID of the jobs", "pid", 'p');
|
||||||
|
|
||||||
if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
|
if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
Job::PrintStatusMode mode = Job::PrintStatusMode::Basic;
|
Job::PrintStatusMode mode = Job::PrintStatusMode::Basic;
|
||||||
|
@ -771,7 +763,7 @@ int Shell::builtin_jobs(int argc, char const** argv)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Shell::builtin_popd(int argc, char const** argv)
|
ErrorOr<int> Shell::builtin_popd(Main::Arguments arguments)
|
||||||
{
|
{
|
||||||
if (directory_stack.size() <= 1) {
|
if (directory_stack.size() <= 1) {
|
||||||
warnln("Shell: popd: directory stack empty");
|
warnln("Shell: popd: directory stack empty");
|
||||||
|
@ -782,7 +774,7 @@ int Shell::builtin_popd(int argc, char const** argv)
|
||||||
Core::ArgsParser parser;
|
Core::ArgsParser parser;
|
||||||
parser.add_option(should_not_switch, "Do not switch dirs", "no-switch", 'n');
|
parser.add_option(should_not_switch, "Do not switch dirs", "no-switch", 'n');
|
||||||
|
|
||||||
if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
|
if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
auto popped_path = directory_stack.take_last();
|
auto popped_path = directory_stack.take_last();
|
||||||
|
@ -799,14 +791,14 @@ int Shell::builtin_popd(int argc, char const** argv)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Shell::builtin_pushd(int argc, char const** argv)
|
ErrorOr<int> Shell::builtin_pushd(Main::Arguments arguments)
|
||||||
{
|
{
|
||||||
StringBuilder path_builder;
|
StringBuilder path_builder;
|
||||||
bool should_switch = true;
|
bool should_switch = true;
|
||||||
|
|
||||||
// From the BASH reference manual: https://www.gnu.org/software/bash/manual/html_node/Directory-Stack-Builtins.html
|
// From the BASH reference manual: https://www.gnu.org/software/bash/manual/html_node/Directory-Stack-Builtins.html
|
||||||
// With no arguments, pushd exchanges the top two directories and makes the new top the current directory.
|
// With no arguments, pushd exchanges the top two directories and makes the new top the current directory.
|
||||||
if (argc == 1) {
|
if (arguments.strings.size() == 1) {
|
||||||
if (directory_stack.size() < 2) {
|
if (directory_stack.size() < 2) {
|
||||||
warnln("pushd: no other directory");
|
warnln("pushd: no other directory");
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -829,26 +821,27 @@ int Shell::builtin_pushd(int argc, char const** argv)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Let's assume the user's typed in 'pushd <dir>'
|
// Let's assume the user's typed in 'pushd <dir>'
|
||||||
if (argc == 2) {
|
if (arguments.strings.size() == 2) {
|
||||||
directory_stack.append(cwd.characters());
|
directory_stack.append(cwd.characters());
|
||||||
if (argv[1][0] == '/') {
|
if (arguments.strings[1].starts_with('/')) {
|
||||||
path_builder.append({ argv[1], strlen(argv[1]) });
|
path_builder.append(arguments.strings[1]);
|
||||||
} else {
|
} else {
|
||||||
path_builder.appendff("{}/{}", cwd, argv[1]);
|
path_builder.appendff("{}/{}", cwd, arguments.strings[1]);
|
||||||
}
|
}
|
||||||
} else if (argc == 3) {
|
} else if (arguments.strings.size() == 3) {
|
||||||
directory_stack.append(cwd.characters());
|
directory_stack.append(cwd.characters());
|
||||||
for (int i = 1; i < argc; i++) {
|
for (size_t i = 1; i < arguments.strings.size(); i++) {
|
||||||
char const* arg = argv[i];
|
auto arg = arguments.strings[i];
|
||||||
|
|
||||||
if (arg[0] != '-') {
|
if (arg.starts_with('-')) {
|
||||||
if (arg[0] == '/') {
|
if (arg.starts_with('/')) {
|
||||||
path_builder.append({ arg, strlen(arg) });
|
path_builder.append(arg);
|
||||||
} else
|
} else {
|
||||||
path_builder.appendff("{}/{}", cwd, arg);
|
path_builder.appendff("{}/{}", cwd, arg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strcmp(arg, "-n"))
|
if (arg == "-n"sv)
|
||||||
should_switch = false;
|
should_switch = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -880,16 +873,16 @@ int Shell::builtin_pushd(int argc, char const** argv)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Shell::builtin_pwd(int, char const**)
|
ErrorOr<int> Shell::builtin_pwd(Main::Arguments)
|
||||||
{
|
{
|
||||||
print_path(cwd);
|
print_path(cwd);
|
||||||
fputc('\n', stdout);
|
fputc('\n', stdout);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Shell::builtin_setopt(int argc, char const** argv)
|
ErrorOr<int> Shell::builtin_setopt(Main::Arguments arguments)
|
||||||
{
|
{
|
||||||
if (argc == 1) {
|
if (arguments.strings.size() == 1) {
|
||||||
#define __ENUMERATE_SHELL_OPTION(name, default_, description) \
|
#define __ENUMERATE_SHELL_OPTION(name, default_, description) \
|
||||||
if (options.name) \
|
if (options.name) \
|
||||||
warnln("{}", #name);
|
warnln("{}", #name);
|
||||||
|
@ -910,7 +903,7 @@ int Shell::builtin_setopt(int argc, char const** argv)
|
||||||
|
|
||||||
#undef __ENUMERATE_SHELL_OPTION
|
#undef __ENUMERATE_SHELL_OPTION
|
||||||
|
|
||||||
if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
|
if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
#define __ENUMERATE_SHELL_OPTION(name, default_, description) \
|
#define __ENUMERATE_SHELL_OPTION(name, default_, description) \
|
||||||
|
@ -926,14 +919,14 @@ int Shell::builtin_setopt(int argc, char const** argv)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Shell::builtin_shift(int argc, char const** argv)
|
ErrorOr<int> Shell::builtin_shift(Main::Arguments arguments)
|
||||||
{
|
{
|
||||||
int count = 1;
|
int count = 1;
|
||||||
|
|
||||||
Core::ArgsParser parser;
|
Core::ArgsParser parser;
|
||||||
parser.add_positional_argument(count, "Shift count", "count", Core::ArgsParser::Required::No);
|
parser.add_positional_argument(count, "Shift count", "count", Core::ArgsParser::Required::No);
|
||||||
|
|
||||||
if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
|
if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (count < 1)
|
if (count < 1)
|
||||||
|
@ -960,7 +953,7 @@ int Shell::builtin_shift(int argc, char const** argv)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Shell::builtin_source(int argc, char const** argv)
|
ErrorOr<int> Shell::builtin_source(Main::Arguments arguments)
|
||||||
{
|
{
|
||||||
char const* file_to_source = nullptr;
|
char const* file_to_source = nullptr;
|
||||||
Vector<StringView> args;
|
Vector<StringView> args;
|
||||||
|
@ -969,7 +962,7 @@ int Shell::builtin_source(int argc, char const** argv)
|
||||||
parser.add_positional_argument(file_to_source, "File to read commands from", "path");
|
parser.add_positional_argument(file_to_source, "File to read commands from", "path");
|
||||||
parser.add_positional_argument(args, "ARGV for the sourced file", "args", Core::ArgsParser::Required::No);
|
parser.add_positional_argument(args, "ARGV for the sourced file", "args", Core::ArgsParser::Required::No);
|
||||||
|
|
||||||
if (!parser.parse(argc, const_cast<char**>(argv)))
|
if (!parser.parse(arguments))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
auto previous_argv = lookup_local_variable("ARGV"sv);
|
auto previous_argv = lookup_local_variable("ARGV"sv);
|
||||||
|
@ -982,7 +975,7 @@ int Shell::builtin_source(int argc, char const** argv)
|
||||||
Vector<String> arguments;
|
Vector<String> arguments;
|
||||||
arguments.ensure_capacity(args.size());
|
arguments.ensure_capacity(args.size());
|
||||||
for (auto& arg : args)
|
for (auto& arg : args)
|
||||||
arguments.append(String::from_utf8(arg).release_value_but_fixme_should_propagate_errors());
|
arguments.append(TRY(String::from_utf8(arg)));
|
||||||
|
|
||||||
set_local_variable("ARGV", AST::make_ref_counted<AST::ListValue>(move(arguments)));
|
set_local_variable("ARGV", AST::make_ref_counted<AST::ListValue>(move(arguments)));
|
||||||
}
|
}
|
||||||
|
@ -993,7 +986,7 @@ int Shell::builtin_source(int argc, char const** argv)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Shell::builtin_time(int argc, char const** argv)
|
ErrorOr<int> Shell::builtin_time(Main::Arguments arguments)
|
||||||
{
|
{
|
||||||
Vector<StringView> args;
|
Vector<StringView> args;
|
||||||
|
|
||||||
|
@ -1004,16 +997,16 @@ int Shell::builtin_time(int argc, char const** argv)
|
||||||
parser.set_stop_on_first_non_option(true);
|
parser.set_stop_on_first_non_option(true);
|
||||||
parser.add_positional_argument(args, "Command to execute with arguments", "command", Core::ArgsParser::Required::Yes);
|
parser.add_positional_argument(args, "Command to execute with arguments", "command", Core::ArgsParser::Required::Yes);
|
||||||
|
|
||||||
if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
|
if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (number_of_iterations < 1)
|
if (number_of_iterations < 1)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
AST::Command command;
|
AST::Command command;
|
||||||
command.argv.ensure_capacity(args.size());
|
TRY(command.argv.try_ensure_capacity(args.size()));
|
||||||
for (auto& arg : args)
|
for (auto& arg : args)
|
||||||
command.argv.append(String::from_utf8(arg).release_value_but_fixme_should_propagate_errors());
|
command.argv.append(TRY(String::from_utf8(arg)));
|
||||||
|
|
||||||
auto commands = expand_aliases({ move(command) });
|
auto commands = expand_aliases({ move(command) });
|
||||||
|
|
||||||
|
@ -1038,7 +1031,7 @@ int Shell::builtin_time(int argc, char const** argv)
|
||||||
|
|
||||||
warnln("Timing report: {} ms", iteration_times.sum());
|
warnln("Timing report: {} ms", iteration_times.sum());
|
||||||
warnln("==============");
|
warnln("==============");
|
||||||
warnln("Command: {}", DeprecatedString::join(' ', Span<char const*>(argv, argc)));
|
warnln("Command: {}", DeprecatedString::join(' ', arguments.strings));
|
||||||
warnln("Average time: {:.2} ms (median: {}, stddev: {:.2}, min: {}, max:{})",
|
warnln("Average time: {:.2} ms (median: {}, stddev: {:.2}, min: {}, max:{})",
|
||||||
iteration_times.average(), iteration_times.median(),
|
iteration_times.average(), iteration_times.median(),
|
||||||
iteration_times.standard_deviation(),
|
iteration_times.standard_deviation(),
|
||||||
|
@ -1052,14 +1045,14 @@ int Shell::builtin_time(int argc, char const** argv)
|
||||||
return exit_code;
|
return exit_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Shell::builtin_umask(int argc, char const** argv)
|
ErrorOr<int> Shell::builtin_umask(Main::Arguments arguments)
|
||||||
{
|
{
|
||||||
char const* mask_text = nullptr;
|
char const* mask_text = nullptr;
|
||||||
|
|
||||||
Core::ArgsParser parser;
|
Core::ArgsParser parser;
|
||||||
parser.add_positional_argument(mask_text, "New mask (omit to get current mask)", "octal-mask", Core::ArgsParser::Required::No);
|
parser.add_positional_argument(mask_text, "New mask (omit to get current mask)", "octal-mask", Core::ArgsParser::Required::No);
|
||||||
|
|
||||||
if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
|
if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (!mask_text) {
|
if (!mask_text) {
|
||||||
|
@ -1080,7 +1073,7 @@ int Shell::builtin_umask(int argc, char const** argv)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Shell::builtin_wait(int argc, char const** argv)
|
ErrorOr<int> Shell::builtin_wait(Main::Arguments arguments)
|
||||||
{
|
{
|
||||||
Vector<int> job_ids;
|
Vector<int> job_ids;
|
||||||
Vector<bool> id_is_pid;
|
Vector<bool> id_is_pid;
|
||||||
|
@ -1110,7 +1103,7 @@ int Shell::builtin_wait(int argc, char const** argv)
|
||||||
return false;
|
return false;
|
||||||
} });
|
} });
|
||||||
|
|
||||||
if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
|
if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
Vector<NonnullRefPtr<Job>> jobs_to_wait_for;
|
Vector<NonnullRefPtr<Job>> jobs_to_wait_for;
|
||||||
|
@ -1138,14 +1131,14 @@ int Shell::builtin_wait(int argc, char const** argv)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Shell::builtin_unset(int argc, char const** argv)
|
ErrorOr<int> Shell::builtin_unset(Main::Arguments arguments)
|
||||||
{
|
{
|
||||||
Vector<DeprecatedString> vars;
|
Vector<DeprecatedString> vars;
|
||||||
|
|
||||||
Core::ArgsParser parser;
|
Core::ArgsParser parser;
|
||||||
parser.add_positional_argument(vars, "List of variables", "variables", Core::ArgsParser::Required::Yes);
|
parser.add_positional_argument(vars, "List of variables", "variables", Core::ArgsParser::Required::Yes);
|
||||||
|
|
||||||
if (!parser.parse(argc, const_cast<char**>(argv), Core::ArgsParser::FailureBehavior::PrintUsage))
|
if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::PrintUsage))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
bool did_touch_path = false;
|
bool did_touch_path = false;
|
||||||
|
@ -1166,15 +1159,21 @@ int Shell::builtin_unset(int argc, char const** argv)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Shell::builtin_not(int argc, char const** argv)
|
ErrorOr<int> Shell::builtin_not(Main::Arguments arguments)
|
||||||
{
|
{
|
||||||
// FIXME: Use ArgsParser when it can collect unrelated -arguments too.
|
Vector<StringView> args;
|
||||||
if (argc == 1)
|
|
||||||
|
Core::ArgsParser parser;
|
||||||
|
parser.set_stop_on_first_non_option(true);
|
||||||
|
parser.add_positional_argument(args, "Command to run followed by its arguments", "string");
|
||||||
|
|
||||||
|
if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::Ignore))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
AST::Command command;
|
AST::Command command;
|
||||||
for (size_t i = 1; i < (size_t)argc; ++i)
|
TRY(command.argv.try_ensure_capacity(args.size()));
|
||||||
command.argv.append(String::from_utf8(StringView { argv[i], strlen(argv[i]) }).release_value_but_fixme_should_propagate_errors());
|
for (auto& arg : args)
|
||||||
|
command.argv.unchecked_append(TRY(String::from_utf8(arg)));
|
||||||
|
|
||||||
auto commands = expand_aliases({ move(command) });
|
auto commands = expand_aliases({ move(command) });
|
||||||
int exit_code = 1;
|
int exit_code = 1;
|
||||||
|
@ -1190,7 +1189,7 @@ int Shell::builtin_not(int argc, char const** argv)
|
||||||
return exit_code == 0 ? 1 : 0;
|
return exit_code == 0 ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Shell::builtin_kill(int argc, char const** argv)
|
ErrorOr<int> Shell::builtin_kill(Main::Arguments arguments)
|
||||||
{
|
{
|
||||||
// Simply translate the arguments and pass them to `kill'
|
// Simply translate the arguments and pass them to `kill'
|
||||||
Vector<String> replaced_values;
|
Vector<String> replaced_values;
|
||||||
|
@ -1199,18 +1198,19 @@ int Shell::builtin_kill(int argc, char const** argv)
|
||||||
warnln("kill: `kill' not found in PATH");
|
warnln("kill: `kill' not found in PATH");
|
||||||
return 126;
|
return 126;
|
||||||
}
|
}
|
||||||
replaced_values.append(String::from_deprecated_string(kill_path.release_value()).release_value_but_fixme_should_propagate_errors());
|
|
||||||
for (auto i = 1; i < argc; ++i) {
|
replaced_values.append(TRY(String::from_deprecated_string(kill_path.release_value())));
|
||||||
if (auto job_id = resolve_job_spec({ argv[i], strlen(argv[1]) }); job_id.has_value()) {
|
for (size_t i = 1; i < arguments.strings.size(); ++i) {
|
||||||
|
if (auto job_id = resolve_job_spec(arguments.strings[i]); job_id.has_value()) {
|
||||||
auto job = find_job(job_id.value());
|
auto job = find_job(job_id.value());
|
||||||
if (job) {
|
if (job) {
|
||||||
replaced_values.append(String::number(job->pid()).release_value_but_fixme_should_propagate_errors());
|
replaced_values.append(TRY(String::number(job->pid())));
|
||||||
} else {
|
} else {
|
||||||
warnln("kill: Job with pid {} not found", job_id.value());
|
warnln("kill: Job with pid {} not found", job_id.value());
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
replaced_values.append(String::from_deprecated_string(argv[i]).release_value_but_fixme_should_propagate_errors());
|
replaced_values.append(TRY(String::from_utf8(arguments.strings[i])));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1233,7 +1233,7 @@ int Shell::builtin_kill(int argc, char const** argv)
|
||||||
return exit_code;
|
return exit_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Shell::run_builtin(const AST::Command& command, NonnullRefPtrVector<AST::Rewiring> const& rewirings, int& retval)
|
ErrorOr<bool> Shell::run_builtin(const AST::Command& command, NonnullRefPtrVector<AST::Rewiring> const& rewirings, int& retval)
|
||||||
{
|
{
|
||||||
if (command.argv.is_empty())
|
if (command.argv.is_empty())
|
||||||
return false;
|
return false;
|
||||||
|
@ -1241,15 +1241,16 @@ bool Shell::run_builtin(const AST::Command& command, NonnullRefPtrVector<AST::Re
|
||||||
if (!has_builtin(command.argv.first()))
|
if (!has_builtin(command.argv.first()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// FIXME: Make all the functions above take the more idiomatic (Main::Arguments) parameters, and remove this gross conversion.
|
Vector<StringView> arguments;
|
||||||
Vector<char const*> argv;
|
TRY(arguments.try_ensure_capacity(command.argv.size()));
|
||||||
Vector<DeprecatedString> args;
|
for (auto& arg : command.argv)
|
||||||
for (auto& arg : command.argv) {
|
arguments.unchecked_append(arg);
|
||||||
args.append(arg.to_deprecated_string());
|
|
||||||
argv.append(args.last().characters());
|
|
||||||
}
|
|
||||||
|
|
||||||
argv.append(nullptr);
|
Main::Arguments arguments_object {
|
||||||
|
.argc = 0,
|
||||||
|
.argv = nullptr,
|
||||||
|
.strings = arguments
|
||||||
|
};
|
||||||
|
|
||||||
StringView name = command.argv.first();
|
StringView name = command.argv.first();
|
||||||
|
|
||||||
|
@ -1271,7 +1272,7 @@ bool Shell::run_builtin(const AST::Command& command, NonnullRefPtrVector<AST::Re
|
||||||
|
|
||||||
#define __ENUMERATE_SHELL_BUILTIN(builtin) \
|
#define __ENUMERATE_SHELL_BUILTIN(builtin) \
|
||||||
if (name == #builtin) { \
|
if (name == #builtin) { \
|
||||||
retval = builtin_##builtin(argv.size() - 1, argv.data()); \
|
retval = TRY(builtin_##builtin(arguments_object)); \
|
||||||
if (!has_error(ShellError::None)) \
|
if (!has_error(ShellError::None)) \
|
||||||
raise_error(m_error, m_error_description, command.position); \
|
raise_error(m_error, m_error_description, command.position); \
|
||||||
fflush(stdout); \
|
fflush(stdout); \
|
||||||
|
@ -1285,7 +1286,7 @@ bool Shell::run_builtin(const AST::Command& command, NonnullRefPtrVector<AST::Re
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Shell::builtin_argsparser_parse(int argc, char const** argv)
|
ErrorOr<int> Shell::builtin_argsparser_parse(Main::Arguments arguments)
|
||||||
{
|
{
|
||||||
// argsparser_parse
|
// argsparser_parse
|
||||||
// --add-option variable [--type (bool | string | i32 | u32 | double | size)] --help-string "" --long-name "" --short-name "" [--value-name "" <if not --type bool>] --list
|
// --add-option variable [--type (bool | string | i32 | u32 | double | size)] --help-string "" --long-name "" --short-name "" [--value-name "" <if not --type bool>] --list
|
||||||
|
@ -1298,7 +1299,7 @@ int Shell::builtin_argsparser_parse(int argc, char const** argv)
|
||||||
|
|
||||||
Core::ArgsParser user_parser;
|
Core::ArgsParser user_parser;
|
||||||
|
|
||||||
Vector<char const*> arguments;
|
Vector<char const*> descriptors;
|
||||||
Variant<Core::ArgsParser::Option, Core::ArgsParser::Arg, Empty> current;
|
Variant<Core::ArgsParser::Option, Core::ArgsParser::Arg, Empty> current;
|
||||||
DeprecatedString current_variable;
|
DeprecatedString current_variable;
|
||||||
// if max > 1 or min < 1, or explicit `--list`.
|
// if max > 1 or min < 1, or explicit `--list`.
|
||||||
|
@ -1314,51 +1315,53 @@ int Shell::builtin_argsparser_parse(int argc, char const** argv)
|
||||||
|
|
||||||
auto type = Type::String;
|
auto type = Type::String;
|
||||||
|
|
||||||
auto try_convert = [](StringView value, Type type) -> Optional<RefPtr<AST::Value>> {
|
auto try_convert = [](StringView value, Type type) -> ErrorOr<Optional<RefPtr<AST::Value>>> {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Type::Bool:
|
case Type::Bool:
|
||||||
return AST::make_ref_counted<AST::StringValue>(String::from_utf8("true"sv).release_value_but_fixme_should_propagate_errors());
|
return AST::make_ref_counted<AST::StringValue>(TRY(String::from_utf8("true"sv)));
|
||||||
case Type::String:
|
case Type::String:
|
||||||
return AST::make_ref_counted<AST::StringValue>(String::from_utf8(value).release_value_but_fixme_should_propagate_errors());
|
return AST::make_ref_counted<AST::StringValue>(TRY(String::from_utf8(value)));
|
||||||
case Type::I32:
|
case Type::I32:
|
||||||
if (auto number = value.to_int(); number.has_value())
|
if (auto number = value.to_int(); number.has_value())
|
||||||
return AST::make_ref_counted<AST::StringValue>(String::number(*number).release_value_but_fixme_should_propagate_errors());
|
return AST::make_ref_counted<AST::StringValue>(TRY(String::number(*number)));
|
||||||
|
|
||||||
warnln("Invalid value for type i32: {}", value);
|
warnln("Invalid value for type i32: {}", value);
|
||||||
return {};
|
return OptionalNone {};
|
||||||
case Type::U32:
|
case Type::U32:
|
||||||
case Type::Size:
|
case Type::Size:
|
||||||
if (auto number = value.to_uint(); number.has_value())
|
if (auto number = value.to_uint(); number.has_value())
|
||||||
return AST::make_ref_counted<AST::StringValue>(String::number(*number).release_value_but_fixme_should_propagate_errors());
|
return AST::make_ref_counted<AST::StringValue>(TRY(String::number(*number)));
|
||||||
|
|
||||||
warnln("Invalid value for type u32|size: {}", value);
|
warnln("Invalid value for type u32|size: {}", value);
|
||||||
return {};
|
return OptionalNone {};
|
||||||
case Type::Double: {
|
case Type::Double: {
|
||||||
DeprecatedString string = value;
|
DeprecatedString string = value;
|
||||||
char* endptr = nullptr;
|
char* endptr = nullptr;
|
||||||
auto number = strtod(string.characters(), &endptr);
|
auto number = strtod(string.characters(), &endptr);
|
||||||
if (endptr != string.characters() + string.length()) {
|
if (endptr != string.characters() + string.length()) {
|
||||||
warnln("Invalid value for type double: {}", value);
|
warnln("Invalid value for type double: {}", value);
|
||||||
return {};
|
return OptionalNone {};
|
||||||
}
|
}
|
||||||
|
|
||||||
return AST::make_ref_counted<AST::StringValue>(String::number(number).release_value_but_fixme_should_propagate_errors());
|
return AST::make_ref_counted<AST::StringValue>(TRY(String::number(number)));
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto enlist = [&](auto name, auto value) -> NonnullRefPtr<AST::Value> {
|
auto enlist = [&](auto name, auto value) -> ErrorOr<NonnullRefPtr<AST::Value>> {
|
||||||
auto variable = lookup_local_variable(name);
|
auto variable = lookup_local_variable(name);
|
||||||
if (variable) {
|
if (variable) {
|
||||||
auto list = const_cast<AST::Value&>(*variable).resolve_as_list(*this).release_value_but_fixme_should_propagate_errors();
|
auto list = TRY(const_cast<AST::Value&>(*variable).resolve_as_list(*this));
|
||||||
auto new_value = value->resolve_as_string(*this).release_value_but_fixme_should_propagate_errors();
|
auto new_value = TRY(value->resolve_as_string(*this));
|
||||||
list.append(move(new_value));
|
list.append(move(new_value));
|
||||||
return make_ref_counted<AST::ListValue>(move(list));
|
return try_make_ref_counted<AST::ListValue>(move(list));
|
||||||
}
|
}
|
||||||
return *value;
|
return *value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// FIXME: We cannot return ErrorOr<T> from Core::ArgsParser::Option::accept_value(), fix the MUST's here whenever that function is ErrorOr-aware.
|
||||||
auto commit = [&] {
|
auto commit = [&] {
|
||||||
return current.visit(
|
return current.visit(
|
||||||
[&](Core::ArgsParser::Option& option) {
|
[&](Core::ArgsParser::Option& option) {
|
||||||
|
@ -1367,11 +1370,11 @@ int Shell::builtin_argsparser_parse(int argc, char const** argv)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
option.accept_value = [&, current_variable, treat_arg_as_list, type](auto value) {
|
option.accept_value = [&, current_variable, treat_arg_as_list, type](auto value) {
|
||||||
auto result = try_convert({ value, strlen(value) }, type);
|
auto result = MUST(try_convert({ value, strlen(value) }, type));
|
||||||
if (result.has_value()) {
|
if (result.has_value()) {
|
||||||
auto value = result.release_value();
|
auto value = result.release_value();
|
||||||
if (treat_arg_as_list)
|
if (treat_arg_as_list)
|
||||||
value = enlist(current_variable, move(value));
|
value = MUST(enlist(current_variable, move(value)));
|
||||||
this->set_local_variable(current_variable, move(value), true);
|
this->set_local_variable(current_variable, move(value), true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1389,11 +1392,11 @@ int Shell::builtin_argsparser_parse(int argc, char const** argv)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
arg.accept_value = [&, current_variable, treat_arg_as_list, type](auto value) {
|
arg.accept_value = [&, current_variable, treat_arg_as_list, type](auto value) {
|
||||||
auto result = try_convert({ value, strlen(value) }, type);
|
auto result = MUST(try_convert({ value, strlen(value) }, type));
|
||||||
if (result.has_value()) {
|
if (result.has_value()) {
|
||||||
auto value = result.release_value();
|
auto value = result.release_value();
|
||||||
if (treat_arg_as_list)
|
if (treat_arg_as_list)
|
||||||
value = enlist(current_variable, move(value));
|
value = MUST(enlist(current_variable, move(value)));
|
||||||
this->set_local_variable(current_variable, move(value), true);
|
this->set_local_variable(current_variable, move(value), true);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1412,7 +1415,7 @@ int Shell::builtin_argsparser_parse(int argc, char const** argv)
|
||||||
|
|
||||||
parser.add_option(Core::ArgsParser::Option {
|
parser.add_option(Core::ArgsParser::Option {
|
||||||
.argument_mode = Core::ArgsParser::OptionArgumentMode::None,
|
.argument_mode = Core::ArgsParser::OptionArgumentMode::None,
|
||||||
.help_string = "Stop processing arguments after a non-argument parameter is seen",
|
.help_string = "Stop processing descriptors after a non-argument parameter is seen",
|
||||||
.long_name = "stop-on-first-non-option",
|
.long_name = "stop-on-first-non-option",
|
||||||
.accept_value = [&](auto) {
|
.accept_value = [&](auto) {
|
||||||
user_parser.set_stop_on_first_non_option(true);
|
user_parser.set_stop_on_first_non_option(true);
|
||||||
|
@ -1496,8 +1499,12 @@ int Shell::builtin_argsparser_parse(int argc, char const** argv)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == Type::Bool)
|
if (type == Type::Bool) {
|
||||||
set_local_variable(current_variable, make_ref_counted<AST::StringValue>(String::from_utf8("false"sv).release_value_but_fixme_should_propagate_errors()), true);
|
set_local_variable(
|
||||||
|
current_variable,
|
||||||
|
make_ref_counted<AST::StringValue>(MUST(String::from_utf8("false"sv))),
|
||||||
|
true);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -1616,7 +1623,7 @@ int Shell::builtin_argsparser_parse(int argc, char const** argv)
|
||||||
});
|
});
|
||||||
parser.add_option(Core::ArgsParser::Option {
|
parser.add_option(Core::ArgsParser::Option {
|
||||||
.argument_mode = Core::ArgsParser::OptionArgumentMode::Required,
|
.argument_mode = Core::ArgsParser::OptionArgumentMode::Required,
|
||||||
.help_string = "Set the minimum required number of positional arguments for the argument being described",
|
.help_string = "Set the minimum required number of positional descriptors for the argument being described",
|
||||||
.long_name = "min",
|
.long_name = "min",
|
||||||
.value_name = "n",
|
.value_name = "n",
|
||||||
.accept_value = [&](auto value) {
|
.accept_value = [&](auto value) {
|
||||||
|
@ -1644,7 +1651,7 @@ int Shell::builtin_argsparser_parse(int argc, char const** argv)
|
||||||
});
|
});
|
||||||
parser.add_option(Core::ArgsParser::Option {
|
parser.add_option(Core::ArgsParser::Option {
|
||||||
.argument_mode = Core::ArgsParser::OptionArgumentMode::Required,
|
.argument_mode = Core::ArgsParser::OptionArgumentMode::Required,
|
||||||
.help_string = "Set the maximum required number of positional arguments for the argument being described",
|
.help_string = "Set the maximum required number of positional descriptors for the argument being described",
|
||||||
.long_name = "max",
|
.long_name = "max",
|
||||||
.value_name = "n",
|
.value_name = "n",
|
||||||
.accept_value = [&](auto value) {
|
.accept_value = [&](auto value) {
|
||||||
|
@ -1687,15 +1694,15 @@ int Shell::builtin_argsparser_parse(int argc, char const** argv)
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
parser.add_positional_argument(arguments, "Arguments to parse via the described ArgsParser configuration", "arg", Core::ArgsParser::Required::No);
|
parser.add_positional_argument(descriptors, "Arguments to parse via the described ArgsParser configuration", "arg", Core::ArgsParser::Required::No);
|
||||||
|
|
||||||
if (!parser.parse(argc, const_cast<char* const*>(argv), Core::ArgsParser::FailureBehavior::Ignore))
|
if (!parser.parse(arguments, Core::ArgsParser::FailureBehavior::Ignore))
|
||||||
return 2;
|
return 2;
|
||||||
|
|
||||||
if (!commit())
|
if (!commit())
|
||||||
return 2;
|
return 2;
|
||||||
|
|
||||||
if (!user_parser.parse(static_cast<int>(arguments.size()), const_cast<char* const*>(arguments.data()), Core::ArgsParser::FailureBehavior::Ignore))
|
if (!user_parser.parse(static_cast<int>(descriptors.size()), const_cast<char* const*>(descriptors.data()), Core::ArgsParser::FailureBehavior::Ignore))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -700,7 +700,7 @@ ErrorOr<RefPtr<Job>> Shell::run_command(const AST::Command& command)
|
||||||
for (auto& redirection : command.redirections)
|
for (auto& redirection : command.redirections)
|
||||||
TRY(resolve_redirection(redirection));
|
TRY(resolve_redirection(redirection));
|
||||||
|
|
||||||
if (int local_return_code = 0; command.should_wait && run_builtin(command, rewirings, local_return_code)) {
|
if (int local_return_code = 0; command.should_wait && TRY(run_builtin(command, rewirings, local_return_code))) {
|
||||||
last_return_code = local_return_code;
|
last_return_code = local_return_code;
|
||||||
for (auto& next_in_chain : command.next_chain)
|
for (auto& next_in_chain : command.next_chain)
|
||||||
run_tail(command, next_in_chain, *last_return_code);
|
run_tail(command, next_in_chain, *last_return_code);
|
||||||
|
@ -791,7 +791,7 @@ ErrorOr<RefPtr<Job>> Shell::run_command(const AST::Command& command)
|
||||||
_exit(last_return_code.value_or(0));
|
_exit(last_return_code.value_or(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (int local_return_code = 0; run_builtin(command, {}, local_return_code))
|
if (int local_return_code = 0; TRY(run_builtin(command, {}, local_return_code)))
|
||||||
_exit(local_return_code);
|
_exit(local_return_code);
|
||||||
|
|
||||||
if (int local_return_code = 0; invoke_function(command, local_return_code))
|
if (int local_return_code = 0; invoke_function(command, local_return_code))
|
||||||
|
@ -877,6 +877,24 @@ ErrorOr<RefPtr<Job>> Shell::run_command(const AST::Command& command)
|
||||||
return *job;
|
return *job;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ErrorOr<void> Shell::execute_process(Span<StringView> argv)
|
||||||
|
{
|
||||||
|
Vector<DeprecatedString> strings;
|
||||||
|
Vector<char const*> args;
|
||||||
|
TRY(strings.try_ensure_capacity(argv.size()));
|
||||||
|
TRY(args.try_ensure_capacity(argv.size() + 1));
|
||||||
|
|
||||||
|
for (auto& entry : argv) {
|
||||||
|
strings.unchecked_append(entry);
|
||||||
|
args.unchecked_append(strings.last().characters());
|
||||||
|
}
|
||||||
|
|
||||||
|
args.append(nullptr);
|
||||||
|
|
||||||
|
// NOTE: noreturn.
|
||||||
|
execute_process(move(args));
|
||||||
|
}
|
||||||
|
|
||||||
void Shell::execute_process(Vector<char const*>&& argv)
|
void Shell::execute_process(Vector<char const*>&& argv)
|
||||||
{
|
{
|
||||||
for (auto& promise : m_active_promises) {
|
for (auto& promise : m_active_promises) {
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include <LibCore/Notifier.h>
|
#include <LibCore/Notifier.h>
|
||||||
#include <LibCore/Object.h>
|
#include <LibCore/Object.h>
|
||||||
#include <LibLine/Editor.h>
|
#include <LibLine/Editor.h>
|
||||||
|
#include <LibMain/Main.h>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
|
|
||||||
#define ENUMERATE_SHELL_BUILTINS() \
|
#define ENUMERATE_SHELL_BUILTINS() \
|
||||||
|
@ -153,7 +154,7 @@ public:
|
||||||
ErrorOr<RefPtr<Job>> run_command(const AST::Command&);
|
ErrorOr<RefPtr<Job>> run_command(const AST::Command&);
|
||||||
NonnullRefPtrVector<Job> run_commands(Vector<AST::Command>&);
|
NonnullRefPtrVector<Job> run_commands(Vector<AST::Command>&);
|
||||||
bool run_file(DeprecatedString const&, bool explicitly_invoked = true);
|
bool run_file(DeprecatedString const&, bool explicitly_invoked = true);
|
||||||
bool run_builtin(const AST::Command&, NonnullRefPtrVector<AST::Rewiring> const&, int& retval);
|
ErrorOr<bool> run_builtin(const AST::Command&, NonnullRefPtrVector<AST::Rewiring> const&, int& retval);
|
||||||
bool has_builtin(StringView) const;
|
bool has_builtin(StringView) const;
|
||||||
RefPtr<AST::Node> run_immediate_function(StringView name, AST::ImmediateExpression& invoking_node, NonnullRefPtrVector<AST::Node> const&);
|
RefPtr<AST::Node> run_immediate_function(StringView name, AST::ImmediateExpression& invoking_node, NonnullRefPtrVector<AST::Node> const&);
|
||||||
static bool has_immediate_function(StringView);
|
static bool has_immediate_function(StringView);
|
||||||
|
@ -417,6 +418,7 @@ private:
|
||||||
void run_tail(const AST::Command&, const AST::NodeWithAction&, int head_exit_code);
|
void run_tail(const AST::Command&, const AST::NodeWithAction&, int head_exit_code);
|
||||||
|
|
||||||
[[noreturn]] void execute_process(Vector<char const*>&& argv);
|
[[noreturn]] void execute_process(Vector<char const*>&& argv);
|
||||||
|
ErrorOr<void> execute_process(Span<StringView> argv);
|
||||||
|
|
||||||
virtual void custom_event(Core::CustomEvent&) override;
|
virtual void custom_event(Core::CustomEvent&) override;
|
||||||
|
|
||||||
|
@ -430,7 +432,7 @@ private:
|
||||||
RefPtr<AST::Node> immediate_length_impl(AST::ImmediateExpression& invoking_node, NonnullRefPtrVector<AST::Node> const&, bool across);
|
RefPtr<AST::Node> immediate_length_impl(AST::ImmediateExpression& invoking_node, NonnullRefPtrVector<AST::Node> const&, bool across);
|
||||||
|
|
||||||
#define __ENUMERATE_SHELL_BUILTIN(builtin) \
|
#define __ENUMERATE_SHELL_BUILTIN(builtin) \
|
||||||
int builtin_##builtin(int argc, char const** argv);
|
ErrorOr<int> builtin_##builtin(Main::Arguments);
|
||||||
|
|
||||||
ENUMERATE_SHELL_BUILTINS();
|
ENUMERATE_SHELL_BUILTINS();
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue