sh: Add a few more shell builtins
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
apio 2023-07-21 21:06:10 +02:00
parent 16b385fc7b
commit 7a4d3ba495
Signed by: apio
GPG Key ID: B8A7D06E42258954
5 changed files with 91 additions and 11 deletions

View File

@ -2,6 +2,9 @@ set(SOURCES
main.cpp
sh.h
builtin/cd.cpp
builtin/exit.cpp
builtin/set.cpp
builtin/unset.cpp
)
add_executable(sh ${SOURCES})

20
shell/builtin/exit.cpp Normal file
View File

@ -0,0 +1,20 @@
#include "sh.h"
#include <os/ArgumentParser.h>
#include <stdlib.h>
shell_builtin_t builtin_exit = [](int argc, char** argv) -> Result<void> {
StringView exit_status;
os::ArgumentParser parser;
parser.set_should_exit_on_bad_usage(false);
parser.add_description("Exit the shell (shell builtin)"_sv);
parser.add_system_program_info("exit"_sv);
parser.add_positional_argument(exit_status, "status"_sv, false);
if (!TRY(parser.parse(argc, argv))) return {};
int status = 0;
if (!exit_status.is_empty()) { status = (int)strtoul(exit_status.chars(), nullptr, 10); }
exit(status);
};

24
shell/builtin/set.cpp Normal file
View File

@ -0,0 +1,24 @@
#include "sh.h"
#include <errno.h>
#include <os/ArgumentParser.h>
#include <stdlib.h>
shell_builtin_t builtin_set = [](int argc, char** argv) -> Result<void> {
StringView name;
StringView value;
bool preserve { false };
os::ArgumentParser parser;
parser.set_should_exit_on_bad_usage(false);
parser.add_description("Set an environment variable (shell builtin)"_sv);
parser.add_system_program_info("set"_sv);
parser.add_positional_argument(name, "name"_sv, true);
parser.add_positional_argument(value, "value"_sv, true);
parser.add_switch_argument(preserve, 'p', "preserve"_sv,
"preserve the environment variable if it already exists"_sv);
if (!TRY(parser.parse(argc, argv))) return {};
if (setenv(name.chars(), value.chars(), !preserve) < 0) return err(errno);
return {};
};

19
shell/builtin/unset.cpp Normal file
View File

@ -0,0 +1,19 @@
#include "sh.h"
#include <errno.h>
#include <os/ArgumentParser.h>
#include <stdlib.h>
shell_builtin_t builtin_unset = [](int argc, char** argv) -> Result<void> {
StringView name;
os::ArgumentParser parser;
parser.set_should_exit_on_bad_usage(false);
parser.add_description("Remove an environment variable (shell builtin)"_sv);
parser.add_system_program_info("unset"_sv);
parser.add_positional_argument(name, "name"_sv, true);
if (!TRY(parser.parse(argc, argv))) return {};
if (unsetenv(name.chars()) < 0) return err(errno);
return {};
};

View File

@ -19,6 +19,9 @@
#include <luna/HashMap.h>
extern shell_builtin_t builtin_cd;
extern shell_builtin_t builtin_exit;
extern shell_builtin_t builtin_set;
extern shell_builtin_t builtin_unset;
static HashMap<StringView, shell_builtin_t> s_builtins;
@ -44,6 +47,25 @@ static Result<void> init_builtins()
s_builtins = {};
TRY(s_builtins.try_set("cd"_sv, builtin_cd));
TRY(s_builtins.try_set("exit"_sv, builtin_exit));
TRY(s_builtins.try_set("set"_sv, builtin_set));
TRY(s_builtins.try_set("unset"_sv, builtin_unset));
return {};
}
static Result<void> builtin_wrapper(const Vector<String>& args, shell_builtin_t builtin)
{
Vector<const char*> argv;
for (const auto& arg : args) { TRY(argv.try_append(arg.chars())); }
TRY(argv.try_append(nullptr));
auto rc = builtin((int)args.size(), const_cast<char**>(argv.data()));
if (rc.has_error())
{
errno = rc.error();
perror(argv[0]);
}
return {};
}
@ -73,6 +95,7 @@ Result<int> luna_main(int argc, char** argv)
parser.add_value_argument(command, 'c', "command"_sv, "execute a single command and then exit"_sv);
parser.parse(argc, argv);
// TODO: This does not properly handle builtins.
if (!command.is_empty()) TRY(execute_command(command));
if (path == "-") { input_file = File::standard_input(); }
@ -103,7 +126,7 @@ Result<int> luna_main(int argc, char** argv)
tcsetpgrp(STDIN_FILENO, getpgid(0));
}
init_builtins();
TRY(init_builtins());
while (1)
{
@ -146,16 +169,7 @@ Result<int> luna_main(int argc, char** argv)
{
auto builtin = maybe_builtin.value();
Vector<const char*> argv;
for (const auto& arg : args) { TRY(argv.try_append(arg.chars())); }
TRY(argv.try_append(nullptr));
auto rc = builtin((int)args.size(), const_cast<char**>(argv.data()));
if (rc.has_error())
{
errno = rc.error();
perror(argv[0]);
}
TRY(builtin_wrapper(args, builtin));
continue;
}