diff --git a/shell/CMakeLists.txt b/shell/CMakeLists.txt index 4d9fffa6..ba1a0be0 100644 --- a/shell/CMakeLists.txt +++ b/shell/CMakeLists.txt @@ -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}) diff --git a/shell/builtin/exit.cpp b/shell/builtin/exit.cpp new file mode 100644 index 00000000..2049d249 --- /dev/null +++ b/shell/builtin/exit.cpp @@ -0,0 +1,20 @@ +#include "sh.h" +#include +#include + +shell_builtin_t builtin_exit = [](int argc, char** argv) -> Result { + 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); +}; diff --git a/shell/builtin/set.cpp b/shell/builtin/set.cpp new file mode 100644 index 00000000..f49b2933 --- /dev/null +++ b/shell/builtin/set.cpp @@ -0,0 +1,24 @@ +#include "sh.h" +#include +#include +#include + +shell_builtin_t builtin_set = [](int argc, char** argv) -> Result { + 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 {}; +}; diff --git a/shell/builtin/unset.cpp b/shell/builtin/unset.cpp new file mode 100644 index 00000000..7153c834 --- /dev/null +++ b/shell/builtin/unset.cpp @@ -0,0 +1,19 @@ +#include "sh.h" +#include +#include +#include + +shell_builtin_t builtin_unset = [](int argc, char** argv) -> Result { + 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 {}; +}; diff --git a/shell/main.cpp b/shell/main.cpp index 2a795633..253cd377 100644 --- a/shell/main.cpp +++ b/shell/main.cpp @@ -19,6 +19,9 @@ #include 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 s_builtins; @@ -44,6 +47,25 @@ static Result 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 builtin_wrapper(const Vector& args, shell_builtin_t builtin) +{ + Vector 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(argv.data())); + if (rc.has_error()) + { + errno = rc.error(); + perror(argv[0]); + } return {}; } @@ -73,6 +95,7 @@ Result 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 luna_main(int argc, char** argv) tcsetpgrp(STDIN_FILENO, getpgid(0)); } - init_builtins(); + TRY(init_builtins()); while (1) { @@ -146,16 +169,7 @@ Result luna_main(int argc, char** argv) { auto builtin = maybe_builtin.value(); - Vector 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(argv.data())); - if (rc.has_error()) - { - errno = rc.error(); - perror(argv[0]); - } + TRY(builtin_wrapper(args, builtin)); continue; }