shell: Split code into multiple files, add the "echo" builtin, and add support for a .shellrc file
All checks were successful
Build and test / build (push) Successful in 1m46s
All checks were successful
Build and test / build (push) Successful in 1m46s
This commit is contained in:
parent
646a15d295
commit
6293aeea58
@ -1,10 +1,15 @@
|
|||||||
|
file(GLOB HEADERS *.h)
|
||||||
|
|
||||||
set(SOURCES
|
set(SOURCES
|
||||||
|
${HEADERS}
|
||||||
main.cpp
|
main.cpp
|
||||||
sh.h
|
Prompt.cpp
|
||||||
|
Command.cpp
|
||||||
builtin/cd.cpp
|
builtin/cd.cpp
|
||||||
builtin/exit.cpp
|
builtin/exit.cpp
|
||||||
builtin/set.cpp
|
builtin/set.cpp
|
||||||
builtin/unset.cpp
|
builtin/unset.cpp
|
||||||
|
builtin/echo.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(sh ${SOURCES})
|
add_executable(sh ${SOURCES})
|
||||||
|
153
shell/Command.cpp
Normal file
153
shell/Command.cpp
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
/**
|
||||||
|
* @file Command.cpp
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief Command parsing and execution.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Command.h"
|
||||||
|
#include <luna/HashMap.h>
|
||||||
|
#include <os/Process.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.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;
|
||||||
|
extern shell_builtin_t builtin_echo;
|
||||||
|
|
||||||
|
static HashMap<StringView, shell_builtin_t> s_builtins;
|
||||||
|
|
||||||
|
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));
|
||||||
|
TRY(s_builtins.try_set("echo"_sv, builtin_echo));
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<Option<String>> read_command(SharedPtr<os::File> file)
|
||||||
|
{
|
||||||
|
auto maybe_cmd = file->read_line();
|
||||||
|
if (maybe_cmd.has_error())
|
||||||
|
{
|
||||||
|
if (maybe_cmd.error() == EINTR)
|
||||||
|
{
|
||||||
|
os::print("\n");
|
||||||
|
return Option<String> { String {} };
|
||||||
|
}
|
||||||
|
return maybe_cmd.release_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cmd = maybe_cmd.release_value();
|
||||||
|
if (cmd.is_empty()) return Option<String> {};
|
||||||
|
|
||||||
|
if (strspn(cmd.chars(), " \n") == cmd.length()) return Option<String> { String {} };
|
||||||
|
|
||||||
|
if (strcspn(cmd.chars(), " #") == 0) return Option<String> { String {} };
|
||||||
|
|
||||||
|
return Option<String> { move(cmd) };
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<OwnedPtr<Command>> parse_command(StringView command)
|
||||||
|
{
|
||||||
|
auto vec = TRY(command.split(" \n"_sv));
|
||||||
|
|
||||||
|
auto cmd = TRY(make_owned<Command>());
|
||||||
|
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> 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> 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<void> run_builtin(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())
|
||||||
|
{
|
||||||
|
os::eprintln("%s: %s", args[0].chars(), rc.error_string());
|
||||||
|
return rc.release_error();
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<int> execute_command_in_subprocess(OwnedPtr<Command> command, SharedPtr<os::File> 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;
|
||||||
|
}
|
36
shell/Command.h
Normal file
36
shell/Command.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* @file Command.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief Command parsing and execution.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "sh.h"
|
||||||
|
#include <luna/OwnedPtr.h>
|
||||||
|
#include <luna/String.h>
|
||||||
|
#include <luna/Vector.h>
|
||||||
|
#include <os/File.h>
|
||||||
|
|
||||||
|
struct Command
|
||||||
|
{
|
||||||
|
Vector<String> args;
|
||||||
|
};
|
||||||
|
|
||||||
|
Result<void> init_builtins();
|
||||||
|
|
||||||
|
Result<Option<String>> read_command(SharedPtr<os::File> file);
|
||||||
|
|
||||||
|
Result<OwnedPtr<Command>> parse_command(StringView command);
|
||||||
|
|
||||||
|
[[noreturn]] void execute_command(OwnedPtr<Command> command);
|
||||||
|
|
||||||
|
[[noreturn]] void execute_command_or_builtin(OwnedPtr<Command> command);
|
||||||
|
|
||||||
|
Result<void> run_builtin(const Vector<String>& args, shell_builtin_t builtin);
|
||||||
|
|
||||||
|
shell_builtin_t* check_builtin(Command& command);
|
||||||
|
|
||||||
|
Result<int> execute_command_in_subprocess(OwnedPtr<Command> command, SharedPtr<os::File> input_file, bool interactive);
|
46
shell/Prompt.cpp
Normal file
46
shell/Prompt.cpp
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/**
|
||||||
|
* @file Prompt.cpp
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief Displays the shell prompt.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Prompt.h"
|
||||||
|
#include <luna/String.h>
|
||||||
|
#include <os/File.h>
|
||||||
|
#include <os/FileSystem.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <sys/utsname.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
struct utsname g_sysinfo;
|
||||||
|
|
||||||
|
const char* hostname = "";
|
||||||
|
const char* username = "";
|
||||||
|
char prompt_end = '$';
|
||||||
|
|
||||||
|
void setup_prompt()
|
||||||
|
{
|
||||||
|
// Set up everything to form a prompt.
|
||||||
|
uname(&g_sysinfo);
|
||||||
|
hostname = g_sysinfo.nodename;
|
||||||
|
|
||||||
|
if (getuid() == 0) prompt_end = '#';
|
||||||
|
|
||||||
|
struct passwd* pw = getpwuid(getuid());
|
||||||
|
if (pw) { username = pw->pw_name; }
|
||||||
|
else { username = getenv("USER"); }
|
||||||
|
endpwent();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> display_prompt()
|
||||||
|
{
|
||||||
|
auto cwd = TRY(os::FileSystem::working_directory());
|
||||||
|
os::print("\x1b[%dm%s\x1b[m@\x1b[36m%s\x1b[m:\x1b[1;34m%s\x1b[m%c ", getuid() == 0 ? 31 : 35, username, hostname,
|
||||||
|
cwd.chars(), prompt_end);
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
14
shell/Prompt.h
Normal file
14
shell/Prompt.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* @file Prompt.h
|
||||||
|
* @author apio (cloudapio.eu)
|
||||||
|
* @brief Displays the shell prompt.
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024, the Luna authors.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <luna/Result.h>
|
||||||
|
|
||||||
|
void setup_prompt();
|
||||||
|
Result<void> display_prompt();
|
20
shell/builtin/echo.cpp
Normal file
20
shell/builtin/echo.cpp
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#include "sh.h"
|
||||||
|
#include <os/ArgumentParser.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
void print_sequenced(const char* string, bool& state)
|
||||||
|
{
|
||||||
|
if (!state) { putchar(' '); }
|
||||||
|
state = false;
|
||||||
|
fputs(string, stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
shell_builtin_t builtin_echo = [](int argc, char** argv) -> Result<void> {
|
||||||
|
bool state = true;
|
||||||
|
|
||||||
|
for (int i = 1; i < argc; i++) { print_sequenced(argv[i], state); }
|
||||||
|
|
||||||
|
putchar('\n');
|
||||||
|
|
||||||
|
return {};
|
||||||
|
};
|
200
shell/main.cpp
200
shell/main.cpp
@ -1,85 +1,73 @@
|
|||||||
|
#include "Command.h"
|
||||||
|
#include "Prompt.h"
|
||||||
#include "sh.h"
|
#include "sh.h"
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <luna/String.h>
|
#include <luna/String.h>
|
||||||
#include <luna/Vector.h>
|
#include <luna/Vector.h>
|
||||||
#include <os/ArgumentParser.h>
|
#include <os/ArgumentParser.h>
|
||||||
|
#include <os/Directory.h>
|
||||||
#include <os/File.h>
|
#include <os/File.h>
|
||||||
#include <os/FileSystem.h>
|
#include <os/FileSystem.h>
|
||||||
#include <os/Process.h>
|
#include <os/Process.h>
|
||||||
#include <pwd.h>
|
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/utsname.h>
|
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <luna/HashMap.h>
|
#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;
|
|
||||||
|
|
||||||
using os::File;
|
using os::File;
|
||||||
|
|
||||||
static Result<Vector<String>> split_command_into_args(StringView cmd)
|
|
||||||
{
|
|
||||||
return cmd.split(" \n"_sv);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Result<void> execute_command(StringView command)
|
|
||||||
{
|
|
||||||
if (strcspn(command.chars(), " #") == 0) return {};
|
|
||||||
|
|
||||||
auto args = TRY(split_command_into_args(command));
|
|
||||||
if (args.size() < 1) exit(0);
|
|
||||||
|
|
||||||
return os::Process::exec(args[0].view(), args.slice());
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do nothing, but we need a handler so read() returns EINTR.
|
// Do nothing, but we need a handler so read() returns EINTR.
|
||||||
static void sigint_handler(int)
|
static void sigint_handler(int)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
struct utsname g_sysinfo;
|
static Result<int> execute_loop(bool interactive, SharedPtr<File> input_file)
|
||||||
|
{
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
if (interactive) display_prompt();
|
||||||
|
|
||||||
const char* hostname = "";
|
auto maybe_cmd = TRY(read_command(input_file));
|
||||||
const char* username = "";
|
if (!maybe_cmd.has_value())
|
||||||
char prompt_end = '$';
|
{
|
||||||
|
if (interactive) puts("exit");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cmd = maybe_cmd.release_value();
|
||||||
|
if (cmd.is_empty()) continue;
|
||||||
|
|
||||||
|
auto command = TRY(parse_command(cmd.view()));
|
||||||
|
if (command->args.size() < 1) continue;
|
||||||
|
|
||||||
|
TRY(execute_command_in_subprocess(move(command), input_file, interactive));
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result<void> execute_rc_file()
|
||||||
|
{
|
||||||
|
static const StringView rc_file_name = ".shellrc"_sv;
|
||||||
|
|
||||||
|
auto home_path = TRY(os::FileSystem::home_directory());
|
||||||
|
auto home_dir = TRY(os::Directory::open(home_path.view()));
|
||||||
|
|
||||||
|
auto rc_file_path = os::Path { home_dir->fd(), rc_file_name };
|
||||||
|
if (os::FileSystem::exists(rc_file_path))
|
||||||
|
{
|
||||||
|
auto rc_file = TRY(os::File::open(rc_file_path, os::File::ReadOnly));
|
||||||
|
|
||||||
|
TRY(execute_loop(false, rc_file));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
Result<int> luna_main(int argc, char** argv)
|
Result<int> luna_main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
@ -97,8 +85,13 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
parser.add_switch_argument(interactive, 'i', "interactive"_sv, "run an interactive shell"_sv);
|
parser.add_switch_argument(interactive, 'i', "interactive"_sv, "run an interactive shell"_sv);
|
||||||
parser.parse(argc, argv);
|
parser.parse(argc, argv);
|
||||||
|
|
||||||
// TODO: This does not properly handle builtins.
|
TRY(init_builtins());
|
||||||
if (!command.is_empty()) TRY(execute_command(command));
|
|
||||||
|
if (!command.is_empty())
|
||||||
|
{
|
||||||
|
auto cmd_obj = TRY(parse_command(command));
|
||||||
|
execute_command_or_builtin(move(cmd_obj));
|
||||||
|
}
|
||||||
|
|
||||||
if (path == "-") { input_file = File::standard_input(); }
|
if (path == "-") { input_file = File::standard_input(); }
|
||||||
else
|
else
|
||||||
@ -111,98 +104,15 @@ Result<int> luna_main(int argc, char** argv)
|
|||||||
|
|
||||||
if (interactive)
|
if (interactive)
|
||||||
{
|
{
|
||||||
// Set up everything to form a prompt.
|
setup_prompt();
|
||||||
uname(&g_sysinfo);
|
|
||||||
hostname = g_sysinfo.nodename;
|
|
||||||
|
|
||||||
if (getuid() == 0) prompt_end = '#';
|
|
||||||
|
|
||||||
struct passwd* pw = getpwuid(getuid());
|
|
||||||
if (pw) { username = pw->pw_name; }
|
|
||||||
else { username = getenv("USER"); }
|
|
||||||
endpwent();
|
|
||||||
|
|
||||||
signal(SIGTTOU, SIG_IGN);
|
signal(SIGTTOU, SIG_IGN);
|
||||||
signal(SIGINT, sigint_handler);
|
signal(SIGINT, sigint_handler);
|
||||||
|
|
||||||
tcsetpgrp(input_file->fd(), getpgid(0));
|
tcsetpgrp(input_file->fd(), getpgid(0));
|
||||||
|
|
||||||
|
TRY(execute_rc_file());
|
||||||
}
|
}
|
||||||
|
|
||||||
TRY(init_builtins());
|
return execute_loop(interactive, input_file);
|
||||||
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
if (interactive)
|
|
||||||
{
|
|
||||||
auto cwd = TRY(os::FileSystem::working_directory());
|
|
||||||
os::print("\x1b[%dm%s\x1b[m@\x1b[36m%s\x1b[m:\x1b[1;34m%s\x1b[m%c ", getuid() == 0 ? 31 : 35, username,
|
|
||||||
hostname, cwd.chars(), prompt_end);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto maybe_cmd = input_file->read_line();
|
|
||||||
if (maybe_cmd.has_error())
|
|
||||||
{
|
|
||||||
if (maybe_cmd.error() == EINTR)
|
|
||||||
{
|
|
||||||
os::print("\n");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
return maybe_cmd.release_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto cmd = maybe_cmd.release_value();
|
|
||||||
if (cmd.is_empty())
|
|
||||||
{
|
|
||||||
if (interactive) puts("exit");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strspn(cmd.chars(), " \n") == cmd.length()) continue;
|
|
||||||
|
|
||||||
if (strcspn(cmd.chars(), " #") == 0) continue;
|
|
||||||
|
|
||||||
auto args = TRY(split_command_into_args(cmd.view()));
|
|
||||||
if (args.size() < 1) continue;
|
|
||||||
|
|
||||||
if (!strchr(args[0].chars(), '/'))
|
|
||||||
{
|
|
||||||
auto maybe_builtin = s_builtins.try_get(args[0].view());
|
|
||||||
|
|
||||||
if (maybe_builtin.has_value())
|
|
||||||
{
|
|
||||||
auto builtin = maybe_builtin.value();
|
|
||||||
|
|
||||||
TRY(builtin_wrapper(args, builtin));
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pid_t child = TRY(os::Process::fork());
|
|
||||||
|
|
||||||
if (child == 0)
|
|
||||||
{
|
|
||||||
if (interactive)
|
|
||||||
{
|
|
||||||
setpgid(0, 0);
|
|
||||||
tcsetpgrp(input_file->fd(), getpid());
|
|
||||||
}
|
|
||||||
TRY(os::Process::exec(args[0].view(), args.slice()));
|
|
||||||
}
|
|
||||||
|
|
||||||
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 0;
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user