/** * @file Command.cpp * @author apio (cloudapio.eu) * @brief Command parsing and execution. * * @copyright Copyright (c) 2024, the Luna authors. * */ #include "Command.h" #include #include #include #include #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; extern shell_builtin_t builtin_echo; static HashMap s_builtins; 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)); TRY(s_builtins.try_set("echo"_sv, builtin_echo)); return {}; } Result> read_command(SharedPtr file) { auto maybe_cmd = file->read_line(); if (maybe_cmd.has_error()) { if (maybe_cmd.error() == EINTR) { os::print("\n"); return Option { String {} }; } return maybe_cmd.release_error(); } auto cmd = maybe_cmd.release_value(); if (cmd.is_empty()) return Option {}; if (strspn(cmd.chars(), " \n") == cmd.length()) return Option { String {} }; if (strcspn(cmd.chars(), " #") == 0) return Option { String {} }; return Option { move(cmd) }; } Result> parse_command(StringView command) { auto vec = TRY(command.split(" \n"_sv)); auto cmd = TRY(make_owned()); cmd->args = move(vec); return cmd; } shell_builtin_t* check_builtin(Command& command) { if (!strchr(command.args[0].chars(), '/')) { return s_builtins.try_get_ref(command.args[0].view()); } return nullptr; } [[noreturn]] void execute_command(OwnedPtr command) { auto err = os::Process::exec(command->args[0].view(), command->args.slice()); os::eprintln("%s: %s", command->args[0].chars(), err.error_string()); os::Process::exit(127); } [[noreturn]] void execute_command_or_builtin(OwnedPtr command) { shell_builtin_t* builtin; if ((builtin = check_builtin(*command))) { auto rc = run_builtin(command->args, *builtin); if (rc.has_error()) os::Process::exit(rc.error()); os::Process::exit(0); } execute_command(move(command)); } Result run_builtin(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()) { os::eprintln("%s: %s", args[0].chars(), rc.error_string()); return rc.release_error(); } return {}; } Result execute_command_in_subprocess(OwnedPtr command, SharedPtr input_file, bool interactive) { shell_builtin_t* builtin; if ((builtin = check_builtin(*command))) { auto rc = run_builtin(command->args, *builtin); if (rc.has_error()) return rc.error(); return 0; } pid_t child = TRY(os::Process::fork()); if (child == 0) { if (interactive) { setpgid(0, 0); tcsetpgrp(input_file->fd(), getpid()); } execute_command(move(command)); } int status; TRY(os::Process::wait(child, &status)); if (interactive) tcsetpgrp(input_file->fd(), getpgid(0)); if (WIFSIGNALED(status)) { int sig = WTERMSIG(status); if (sig != SIGINT && sig != SIGQUIT) os::println("[sh] Process %d exited: %s", child, strsignal(sig)); else os::print("\n"); } return status; }