2022-12-30 20:48:15 +02:00
|
|
|
#include <BAN/Math.h>
|
2022-12-13 21:34:50 +02:00
|
|
|
#include <BAN/StringView.h>
|
|
|
|
#include <BAN/Vector.h>
|
2022-12-15 17:29:35 +02:00
|
|
|
#include <kernel/CPUID.h>
|
2022-12-30 19:38:21 +02:00
|
|
|
#include <kernel/Input.h>
|
2022-12-13 21:34:50 +02:00
|
|
|
#include <kernel/IO.h>
|
2022-12-15 17:29:35 +02:00
|
|
|
#include <kernel/PIT.h>
|
2022-12-13 21:34:50 +02:00
|
|
|
#include <kernel/RTC.h>
|
2023-02-01 21:05:44 +02:00
|
|
|
#include <kernel/Scheduler.h>
|
2022-12-30 19:38:21 +02:00
|
|
|
#include <kernel/Shell.h>
|
2022-12-13 21:34:50 +02:00
|
|
|
|
2023-02-20 01:46:00 +02:00
|
|
|
#include <kernel/FS/VirtualFileSystem.h>
|
|
|
|
|
2023-01-16 20:08:13 +02:00
|
|
|
#include <ctype.h>
|
|
|
|
|
2023-02-01 21:05:44 +02:00
|
|
|
#define TTY_PRINT(...) Formatter::print([this](char c) { m_tty->putchar(c); }, __VA_ARGS__)
|
|
|
|
#define TTY_PRINTLN(...) Formatter::println([this](char c) { m_tty->putchar(c); }, __VA_ARGS__)
|
2022-12-27 20:11:49 +02:00
|
|
|
|
2022-12-13 21:34:50 +02:00
|
|
|
namespace Kernel
|
|
|
|
{
|
2022-12-30 20:48:15 +02:00
|
|
|
using namespace BAN;
|
2022-12-13 21:34:50 +02:00
|
|
|
|
2023-01-16 15:16:39 +02:00
|
|
|
static auto s_default_prompt = "\\[\e[32m\\]user\\[\e[m\\]# "_sv;
|
2022-12-13 21:34:50 +02:00
|
|
|
|
2023-01-16 15:16:39 +02:00
|
|
|
Shell::Shell(TTY* tty)
|
|
|
|
: m_tty(tty)
|
2022-12-13 21:34:50 +02:00
|
|
|
{
|
2023-02-01 21:05:44 +02:00
|
|
|
Input::register_key_event_callback({ &Shell::key_event_callback, this });
|
|
|
|
set_prompt(s_default_prompt);
|
|
|
|
MUST(m_buffer.push_back(""_sv));
|
2022-12-13 21:34:50 +02:00
|
|
|
}
|
|
|
|
|
2023-02-01 21:05:44 +02:00
|
|
|
void Shell::set_prompt(StringView prompt)
|
2022-12-13 21:34:50 +02:00
|
|
|
{
|
2023-01-13 15:07:24 +02:00
|
|
|
m_prompt_length = 0;
|
|
|
|
m_prompt = String();
|
|
|
|
|
|
|
|
bool skipping = false;
|
2023-02-01 21:05:44 +02:00
|
|
|
for (size_t i = 0; i < prompt.size(); i++)
|
2023-01-13 15:07:24 +02:00
|
|
|
{
|
2023-02-01 21:05:44 +02:00
|
|
|
if (i < prompt.size() - 1 && prompt[i] == '\\')
|
2023-01-13 15:07:24 +02:00
|
|
|
{
|
|
|
|
if (prompt[i + 1] == '[')
|
|
|
|
skipping = true;
|
|
|
|
if (prompt[i + 1] == ']')
|
|
|
|
skipping = false;
|
|
|
|
i++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2023-02-01 21:05:44 +02:00
|
|
|
MUST(m_prompt.push_back(prompt[i]));
|
2023-01-13 15:07:24 +02:00
|
|
|
if (!skipping)
|
|
|
|
m_prompt_length++;
|
|
|
|
}
|
2022-12-13 21:34:50 +02:00
|
|
|
}
|
|
|
|
|
2023-02-01 21:05:44 +02:00
|
|
|
void Shell::run()
|
2022-12-13 21:34:50 +02:00
|
|
|
{
|
2023-01-13 15:07:24 +02:00
|
|
|
TTY_PRINT("{}", m_prompt);
|
2022-12-13 21:34:50 +02:00
|
|
|
for (;;)
|
|
|
|
{
|
2023-03-07 23:22:25 +02:00
|
|
|
PIT::sleep(1); // sleep until next reschedule
|
2022-12-30 19:38:21 +02:00
|
|
|
Input::update();
|
2022-12-13 21:34:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-01 21:05:44 +02:00
|
|
|
Vector<String> Shell::parse_arguments(StringView command) const
|
2023-01-16 20:08:13 +02:00
|
|
|
{
|
|
|
|
Vector<String> result;
|
|
|
|
|
2023-02-01 21:05:44 +02:00
|
|
|
while (!command.empty())
|
2023-01-16 20:08:13 +02:00
|
|
|
{
|
2023-02-01 21:05:44 +02:00
|
|
|
while (!command.empty() && isspace(command.front()))
|
|
|
|
command = command.substring(1);
|
2023-01-16 20:08:13 +02:00
|
|
|
|
2023-02-01 21:05:44 +02:00
|
|
|
if (command.empty())
|
2023-01-16 20:08:13 +02:00
|
|
|
break;
|
|
|
|
|
2023-02-01 21:05:44 +02:00
|
|
|
MUST(result.push_back(""_sv));
|
2023-01-16 20:08:13 +02:00
|
|
|
|
|
|
|
char quoted = '\0';
|
|
|
|
bool escape = false;
|
2023-02-01 21:05:44 +02:00
|
|
|
while (!command.empty())
|
2023-01-16 20:08:13 +02:00
|
|
|
{
|
2023-02-01 21:05:44 +02:00
|
|
|
char ch = command.front();
|
2023-01-16 20:08:13 +02:00
|
|
|
switch (ch)
|
|
|
|
{
|
|
|
|
case '"':
|
|
|
|
case '\'':
|
|
|
|
if (!quoted)
|
|
|
|
quoted = ch;
|
|
|
|
else if (ch == quoted)
|
|
|
|
quoted = '\0';
|
|
|
|
else
|
|
|
|
goto default_case;
|
|
|
|
break;
|
|
|
|
case '\\':
|
|
|
|
if (escape)
|
|
|
|
goto default_case;
|
|
|
|
escape = true;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
default_case:
|
|
|
|
if (isspace(ch) && !quoted && !escape)
|
|
|
|
goto argument_done;
|
|
|
|
if (quoted && escape)
|
|
|
|
{
|
|
|
|
switch (ch)
|
|
|
|
{
|
2023-02-01 21:05:44 +02:00
|
|
|
case 'f': MUST(result.back().push_back('\f')); break;
|
|
|
|
case 'n': MUST(result.back().push_back('\n')); break;
|
|
|
|
case 'r': MUST(result.back().push_back('\r')); break;
|
|
|
|
case 't': MUST(result.back().push_back('\t')); break;
|
|
|
|
case 'v': MUST(result.back().push_back('\v')); break;
|
|
|
|
case '"': MUST(result.back().push_back('"')); break;
|
|
|
|
case '\'': MUST(result.back().push_back('\'')); break;
|
|
|
|
case '\\': MUST(result.back().push_back('\\')); break;
|
2023-01-16 20:08:13 +02:00
|
|
|
default:
|
|
|
|
char buffer[3] { '\\', ch, '\0' };
|
2023-02-01 21:05:44 +02:00
|
|
|
MUST(result.back().append(buffer));
|
2023-01-16 20:08:13 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2023-02-01 21:05:44 +02:00
|
|
|
MUST(result.back().push_back(ch));
|
2023-01-16 20:08:13 +02:00
|
|
|
}
|
|
|
|
escape = false;
|
|
|
|
break;
|
|
|
|
}
|
2023-02-01 21:05:44 +02:00
|
|
|
command = command.substring(1);
|
2023-01-16 20:08:13 +02:00
|
|
|
}
|
|
|
|
argument_done:
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2023-03-08 17:05:37 +02:00
|
|
|
BAN::ErrorOr<void> Shell::process_command(const Vector<String>& arguments)
|
2022-12-13 21:34:50 +02:00
|
|
|
{
|
2023-02-01 21:05:44 +02:00
|
|
|
if (arguments.empty())
|
2022-12-30 20:48:15 +02:00
|
|
|
{
|
2022-12-13 21:34:50 +02:00
|
|
|
|
2022-12-30 20:48:15 +02:00
|
|
|
}
|
2023-02-01 21:05:44 +02:00
|
|
|
else if (arguments.front() == "date")
|
2022-12-13 21:34:50 +02:00
|
|
|
{
|
2023-02-01 21:05:44 +02:00
|
|
|
if (arguments.size() != 1)
|
2023-03-08 17:05:37 +02:00
|
|
|
return BAN::Error::from_c_string("'date' does not support command line arguments");
|
2023-02-01 21:05:44 +02:00
|
|
|
auto time = RTC::get_current_time();
|
2022-12-27 20:11:49 +02:00
|
|
|
TTY_PRINTLN("{}", time);
|
2022-12-13 21:34:50 +02:00
|
|
|
}
|
2023-02-01 21:05:44 +02:00
|
|
|
else if (arguments.front() == "echo")
|
2022-12-13 21:34:50 +02:00
|
|
|
{
|
2023-02-01 21:05:44 +02:00
|
|
|
if (arguments.size() > 1)
|
2022-12-13 21:34:50 +02:00
|
|
|
{
|
2023-02-22 21:55:32 +02:00
|
|
|
TTY_PRINT("{}", arguments[1]);
|
2023-02-01 21:05:44 +02:00
|
|
|
for (size_t i = 2; i < arguments.size(); i++)
|
2022-12-27 20:11:49 +02:00
|
|
|
TTY_PRINT(" {}", arguments[i]);
|
2022-12-13 21:34:50 +02:00
|
|
|
}
|
2022-12-27 20:11:49 +02:00
|
|
|
TTY_PRINTLN("");
|
2022-12-13 21:34:50 +02:00
|
|
|
}
|
2023-02-01 21:05:44 +02:00
|
|
|
else if (arguments.front() == "clear")
|
2022-12-13 21:34:50 +02:00
|
|
|
{
|
2023-02-01 21:05:44 +02:00
|
|
|
if (arguments.size() != 1)
|
2023-03-08 17:05:37 +02:00
|
|
|
return BAN::Error::from_c_string("'clear' does not support command line arguments");
|
2023-02-01 21:05:44 +02:00
|
|
|
m_tty->clear();
|
|
|
|
m_tty->set_cursor_position(0, 0);
|
2022-12-13 21:34:50 +02:00
|
|
|
}
|
2023-02-01 21:05:44 +02:00
|
|
|
else if (arguments.front() == "time")
|
2022-12-15 17:29:35 +02:00
|
|
|
{
|
|
|
|
auto new_args = arguments;
|
2023-02-01 21:05:44 +02:00
|
|
|
new_args.remove(0);
|
2022-12-15 17:29:35 +02:00
|
|
|
auto start = PIT::ms_since_boot();
|
2023-03-08 17:05:37 +02:00
|
|
|
TRY(process_command(new_args));
|
2022-12-15 17:29:35 +02:00
|
|
|
auto duration = PIT::ms_since_boot() - start;
|
2022-12-27 20:11:49 +02:00
|
|
|
TTY_PRINTLN("took {} ms", duration);
|
2022-12-15 17:29:35 +02:00
|
|
|
}
|
2023-02-01 21:05:44 +02:00
|
|
|
else if (arguments.front() == "thread")
|
2023-01-16 15:17:00 +02:00
|
|
|
{
|
2023-03-09 15:25:39 +02:00
|
|
|
struct thread_data_t
|
|
|
|
{
|
|
|
|
Shell* shell;
|
|
|
|
SpinLock& lock;
|
|
|
|
const Vector<String>& arguments;
|
|
|
|
};
|
|
|
|
|
|
|
|
auto function = [](void* data)
|
|
|
|
{
|
|
|
|
thread_data_t* thread_data = (thread_data_t*)data;
|
|
|
|
Shell* shell = thread_data->shell;
|
|
|
|
auto args = thread_data->arguments;
|
|
|
|
thread_data->lock.unlock();
|
|
|
|
|
|
|
|
args.remove(0);
|
|
|
|
PIT::sleep(5000);
|
|
|
|
|
|
|
|
if (auto res = shell->process_command(args); res.is_error())
|
|
|
|
Formatter::println([&](char c) { shell->m_tty->putchar(c); }, "{}", res.error());
|
|
|
|
};
|
|
|
|
|
|
|
|
SpinLock spinlock;
|
|
|
|
thread_data_t thread_data = { this, spinlock, arguments };
|
|
|
|
spinlock.lock();
|
|
|
|
TRY(Scheduler::get().add_thread(TRY(Thread::create(function, &thread_data))));
|
|
|
|
while (spinlock.is_locked());
|
2023-02-01 21:05:44 +02:00
|
|
|
}
|
|
|
|
else if (arguments.front() == "memory")
|
|
|
|
{
|
|
|
|
if (arguments.size() != 1)
|
2023-03-08 17:05:37 +02:00
|
|
|
return BAN::Error::from_c_string("'memory' does not support command line arguments");
|
2023-01-16 15:17:00 +02:00
|
|
|
kmalloc_dump_info();
|
|
|
|
}
|
2023-02-19 17:54:11 +02:00
|
|
|
else if (arguments.front() == "sleep")
|
|
|
|
{
|
|
|
|
if (arguments.size() != 1)
|
2023-03-08 17:05:37 +02:00
|
|
|
return BAN::Error::from_c_string("'sleep' does not support command line arguments");
|
2023-02-19 17:54:11 +02:00
|
|
|
PIT::sleep(5000);
|
|
|
|
}
|
2023-02-01 21:05:44 +02:00
|
|
|
else if (arguments.front() == "cpuinfo")
|
2022-12-15 17:29:35 +02:00
|
|
|
{
|
2023-02-01 21:05:44 +02:00
|
|
|
if (arguments.size() != 1)
|
2023-03-08 17:05:37 +02:00
|
|
|
return BAN::Error::from_c_string("'cpuinfo' does not support command line arguments");
|
2022-12-15 17:29:35 +02:00
|
|
|
|
|
|
|
uint32_t ecx, edx;
|
2023-02-01 21:05:44 +02:00
|
|
|
auto vendor = CPUID::get_vendor();
|
|
|
|
CPUID::get_features(ecx, edx);
|
2022-12-15 17:29:35 +02:00
|
|
|
|
2022-12-27 20:11:49 +02:00
|
|
|
TTY_PRINTLN("Vendor: '{}'", vendor);
|
2023-02-01 21:05:44 +02:00
|
|
|
TTY_PRINTLN("64-bit: {}", CPUID::is_64_bit());
|
2022-12-15 17:29:35 +02:00
|
|
|
bool first = true;
|
|
|
|
for (int i = 0; i < 32; i++)
|
|
|
|
if (ecx & ((uint32_t)1 << i))
|
2023-02-01 21:05:44 +02:00
|
|
|
TTY_PRINT("{}{}", first ? (first = false, "") : ", ", CPUID::feature_string_ecx((uint32_t)1 << i));
|
2022-12-15 17:29:35 +02:00
|
|
|
for (int i = 0; i < 32; i++)
|
|
|
|
if (edx & ((uint32_t)1 << i))
|
2023-02-01 21:05:44 +02:00
|
|
|
TTY_PRINT("{}{}", first ? (first = false, "") : ", ", CPUID::feature_string_edx((uint32_t)1 << i));
|
2022-12-15 17:29:35 +02:00
|
|
|
if (!first)
|
2022-12-27 20:11:49 +02:00
|
|
|
TTY_PRINTLN("");
|
2022-12-15 17:29:35 +02:00
|
|
|
}
|
2023-02-01 21:05:44 +02:00
|
|
|
else if (arguments.front() == "random")
|
2022-12-15 17:29:35 +02:00
|
|
|
{
|
2023-02-01 21:05:44 +02:00
|
|
|
if (arguments.size() != 1)
|
2023-03-08 17:05:37 +02:00
|
|
|
return BAN::Error::from_c_string("'random' does not support command line arguments");
|
2022-12-15 17:29:35 +02:00
|
|
|
uint32_t ecx, edx;
|
2023-02-01 21:05:44 +02:00
|
|
|
CPUID::get_features(ecx, edx);
|
2022-12-15 17:29:35 +02:00
|
|
|
if (!(ecx & CPUID::Features::ECX_RDRND))
|
2023-03-08 17:05:37 +02:00
|
|
|
return BAN::Error::from_c_string("cpu does not support RDRAND instruction");
|
2022-12-15 17:29:35 +02:00
|
|
|
|
|
|
|
for (int i = 0; i < 10; i++)
|
|
|
|
{
|
|
|
|
uint32_t random;
|
|
|
|
asm volatile("rdrand %0" : "=r"(random));
|
2022-12-27 20:11:49 +02:00
|
|
|
TTY_PRINTLN(" 0x{8H}", random);
|
2022-12-15 17:29:35 +02:00
|
|
|
}
|
|
|
|
}
|
2023-02-01 21:05:44 +02:00
|
|
|
else if (arguments.front() == "reboot")
|
2022-12-13 21:34:50 +02:00
|
|
|
{
|
2023-02-01 21:05:44 +02:00
|
|
|
if (arguments.size() != 1)
|
2023-03-08 17:05:37 +02:00
|
|
|
return BAN::Error::from_c_string("'reboot' does not support command line arguments");
|
2022-12-13 21:34:50 +02:00
|
|
|
uint8_t good = 0x02;
|
|
|
|
while (good & 0x02)
|
|
|
|
good = IO::inb(0x64);
|
|
|
|
IO::outb(0x64, 0xFE);
|
|
|
|
asm volatile("cli; hlt");
|
2022-12-30 20:48:15 +02:00
|
|
|
}
|
2023-03-07 18:56:08 +02:00
|
|
|
else if (arguments.front() == "lspci")
|
|
|
|
{
|
|
|
|
if (arguments.size() != 1)
|
2023-03-08 17:05:37 +02:00
|
|
|
return BAN::Error::from_c_string("'lspci' does not support command line arguments");
|
2023-03-07 18:56:08 +02:00
|
|
|
for (auto& device : PCI::get().devices())
|
|
|
|
TTY_PRINTLN("{2H}:{2H}.{2H} {2H}", device.bus(), device.dev(), device.func(), device.class_code());
|
|
|
|
}
|
2023-02-20 01:46:00 +02:00
|
|
|
else if (arguments.front() == "ls")
|
|
|
|
{
|
|
|
|
if (arguments.size() > 2)
|
2023-03-08 17:05:37 +02:00
|
|
|
return BAN::Error::from_c_string("usage: 'ls [path]'");
|
2023-02-20 01:46:00 +02:00
|
|
|
|
|
|
|
BAN::StringView path = (arguments.size() == 2) ? arguments[1].sv() : "/";
|
|
|
|
if (path.front() != '/')
|
2023-03-08 17:05:37 +02:00
|
|
|
return BAN::Error::from_c_string("ls currently works only with absolute paths");
|
2023-02-26 02:57:27 +02:00
|
|
|
|
2023-03-08 17:05:37 +02:00
|
|
|
auto directory = TRY(VirtualFileSystem::get().from_absolute_path(path));
|
|
|
|
auto inodes = TRY(directory->directory_inodes());
|
2023-02-20 01:46:00 +02:00
|
|
|
|
2023-02-20 21:39:24 +02:00
|
|
|
auto mode_string = [](Inode::Mode mode)
|
|
|
|
{
|
|
|
|
static char buffer[11] {};
|
|
|
|
buffer[0] = mode.IFDIR ? 'd' : '-';
|
|
|
|
buffer[1] = mode.IRUSR ? 'r' : '-';
|
|
|
|
buffer[2] = mode.IWUSR ? 'w' : '-';
|
|
|
|
buffer[3] = mode.IXUSR ? 'x' : '-';
|
|
|
|
buffer[4] = mode.IRGRP ? 'r' : '-';
|
|
|
|
buffer[5] = mode.IWGRP ? 'w' : '-';
|
|
|
|
buffer[6] = mode.IXGRP ? 'x' : '-';
|
|
|
|
buffer[7] = mode.IROTH ? 'r' : '-';
|
|
|
|
buffer[8] = mode.IWOTH ? 'w' : '-';
|
|
|
|
buffer[9] = mode.IXOTH ? 'x' : '-';
|
|
|
|
return (const char*)buffer;
|
|
|
|
};
|
|
|
|
|
2023-02-22 21:55:32 +02:00
|
|
|
TTY_PRINTLN("{}", path);
|
2023-02-20 01:46:00 +02:00
|
|
|
for (auto& inode : inodes)
|
2023-02-20 21:39:24 +02:00
|
|
|
if (inode->ifdir())
|
|
|
|
TTY_PRINTLN(" {} {7} \e[34m{}\e[m", mode_string(inode->mode()), inode->size(), inode->name());
|
2023-02-20 01:46:00 +02:00
|
|
|
for (auto& inode : inodes)
|
2023-02-20 21:39:24 +02:00
|
|
|
if (!inode->ifdir())
|
|
|
|
TTY_PRINTLN(" {} {7} {}", mode_string(inode->mode()), inode->size(), inode->name());
|
2023-02-20 01:46:00 +02:00
|
|
|
}
|
2023-02-20 10:25:15 +02:00
|
|
|
else if (arguments.front() == "cat")
|
|
|
|
{
|
2023-02-22 21:55:32 +02:00
|
|
|
if (arguments.size() != 2)
|
2023-03-08 17:05:37 +02:00
|
|
|
return BAN::Error::from_c_string("usage: 'cat path'");
|
2023-02-20 10:25:15 +02:00
|
|
|
|
2023-03-08 17:05:37 +02:00
|
|
|
auto file = TRY(VirtualFileSystem::get().from_absolute_path(arguments[1]));
|
|
|
|
auto data = TRY(file->read_all());
|
2023-02-20 10:25:15 +02:00
|
|
|
TTY_PRINTLN("{}", BAN::StringView((const char*)data.data(), data.size()));
|
|
|
|
}
|
2023-02-22 22:29:31 +02:00
|
|
|
else if (arguments.front() == "loadfont")
|
|
|
|
{
|
|
|
|
if (arguments.size() != 2)
|
2023-03-08 17:05:37 +02:00
|
|
|
return BAN::Error::from_c_string("usage: 'loadfont font_path'");
|
2023-02-22 22:29:31 +02:00
|
|
|
|
2023-03-08 17:05:37 +02:00
|
|
|
auto font = TRY(Font::load(arguments[1]));
|
2023-02-22 22:29:31 +02:00
|
|
|
m_tty->set_font(font);
|
|
|
|
}
|
2022-12-30 20:48:15 +02:00
|
|
|
else
|
|
|
|
{
|
2023-03-08 17:05:37 +02:00
|
|
|
return BAN::Error::from_format("unrecognized command '{}'", arguments.front());
|
2022-12-13 21:34:50 +02:00
|
|
|
}
|
|
|
|
|
2023-03-08 17:05:37 +02:00
|
|
|
return {};
|
2022-12-13 21:34:50 +02:00
|
|
|
}
|
|
|
|
|
2023-02-01 21:05:44 +02:00
|
|
|
void Shell::rerender_buffer() const
|
2022-12-16 03:53:55 +02:00
|
|
|
{
|
2023-01-13 15:07:24 +02:00
|
|
|
TTY_PRINT("\e[{}G{}\e[K", m_prompt_length + 1, m_buffer[m_cursor_pos.line]);
|
2022-12-16 11:53:59 +02:00
|
|
|
}
|
2022-12-16 03:53:55 +02:00
|
|
|
|
2023-02-01 21:05:44 +02:00
|
|
|
static uint32_t get_last_length(StringView sv)
|
2022-12-16 11:53:59 +02:00
|
|
|
{
|
2023-02-01 21:05:44 +02:00
|
|
|
if (sv.size() >= 2 && ((uint8_t)sv[sv.size() - 2] >> 5) == 0b110) return 2;
|
|
|
|
if (sv.size() >= 3 && ((uint8_t)sv[sv.size() - 3] >> 4) == 0b1110) return 3;
|
|
|
|
if (sv.size() >= 4 && ((uint8_t)sv[sv.size() - 4] >> 3) == 0b11110) return 4;
|
|
|
|
return Math::min<uint32_t>(sv.size(), 1);
|
2023-01-13 15:07:24 +02:00
|
|
|
}
|
2022-12-16 03:53:55 +02:00
|
|
|
|
2023-02-01 21:05:44 +02:00
|
|
|
static uint32_t get_next_length(StringView sv)
|
2023-01-13 15:07:24 +02:00
|
|
|
{
|
2023-02-01 21:05:44 +02:00
|
|
|
if (sv.size() >= 2 && ((uint8_t)sv[0] >> 5) == 0b110) return 2;
|
|
|
|
if (sv.size() >= 3 && ((uint8_t)sv[0] >> 4) == 0b1110) return 3;
|
|
|
|
if (sv.size() >= 4 && ((uint8_t)sv[0] >> 3) == 0b11110) return 4;
|
|
|
|
return Math::min<uint32_t>(sv.size(), 1);
|
2023-01-13 15:07:24 +02:00
|
|
|
}
|
2022-12-16 03:53:55 +02:00
|
|
|
|
2023-02-01 21:05:44 +02:00
|
|
|
static uint32_t get_unicode_character_count(StringView sv)
|
2023-01-13 15:07:24 +02:00
|
|
|
{
|
|
|
|
uint32_t len = 0;
|
2023-02-01 21:05:44 +02:00
|
|
|
for (uint32_t i = 0; i < sv.size(); i++)
|
2023-01-13 15:07:24 +02:00
|
|
|
{
|
|
|
|
uint8_t ch = sv[i];
|
|
|
|
if ((ch >> 5) == 0b110) i += 1;
|
|
|
|
if ((ch >> 4) == 0b1110) i += 2;
|
|
|
|
if ((ch >> 3) == 0b11110) i += 3;
|
|
|
|
len++;
|
2022-12-16 03:53:55 +02:00
|
|
|
}
|
2023-01-13 15:07:24 +02:00
|
|
|
return len;
|
2022-12-16 03:53:55 +02:00
|
|
|
}
|
|
|
|
|
2023-02-01 21:05:44 +02:00
|
|
|
void Shell::key_event_callback(Input::KeyEvent event)
|
2022-12-13 21:34:50 +02:00
|
|
|
{
|
|
|
|
if (!event.pressed)
|
|
|
|
return;
|
2022-12-15 17:29:35 +02:00
|
|
|
|
2023-01-13 15:07:24 +02:00
|
|
|
String& current_buffer = m_buffer[m_cursor_pos.line];
|
|
|
|
|
2022-12-13 21:34:50 +02:00
|
|
|
switch (event.key)
|
|
|
|
{
|
2022-12-30 19:38:21 +02:00
|
|
|
case Input::Key::Backspace:
|
2023-01-13 15:07:24 +02:00
|
|
|
if (m_cursor_pos.col > 0)
|
2022-12-13 21:34:50 +02:00
|
|
|
{
|
2023-02-01 21:05:44 +02:00
|
|
|
TTY_PRINT("\e[D{} ", current_buffer.sv().substring(m_cursor_pos.index));
|
2022-12-16 03:53:55 +02:00
|
|
|
|
2023-02-01 21:05:44 +02:00
|
|
|
uint32_t len = get_last_length(current_buffer.sv().substring(0, m_cursor_pos.index));
|
2023-01-13 15:07:24 +02:00
|
|
|
m_cursor_pos.index -= len;
|
2023-02-01 21:05:44 +02:00
|
|
|
current_buffer.erase(m_cursor_pos.index, len);
|
2023-01-13 15:07:24 +02:00
|
|
|
m_cursor_pos.col--;
|
2022-12-13 21:34:50 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2022-12-30 19:38:21 +02:00
|
|
|
case Input::Key::Enter:
|
|
|
|
case Input::Key::NumpadEnter:
|
2022-12-13 21:34:50 +02:00
|
|
|
{
|
2023-01-13 15:07:24 +02:00
|
|
|
TTY_PRINTLN("");
|
2023-02-01 21:05:44 +02:00
|
|
|
auto arguments = parse_arguments(current_buffer.sv());
|
|
|
|
if (!arguments.empty())
|
2023-01-13 15:07:24 +02:00
|
|
|
{
|
2023-03-08 17:05:37 +02:00
|
|
|
if (auto res = process_command(arguments); res.is_error())
|
|
|
|
TTY_PRINTLN("{}", res.error());
|
2023-02-01 21:05:44 +02:00
|
|
|
MUST(m_old_buffer.push_back(current_buffer));
|
2023-01-13 15:07:24 +02:00
|
|
|
m_buffer = m_old_buffer;
|
2023-02-01 21:05:44 +02:00
|
|
|
MUST(m_buffer.push_back(""_sv));
|
|
|
|
m_cursor_pos.line = m_buffer.size() - 1;
|
2023-01-13 15:07:24 +02:00
|
|
|
}
|
|
|
|
m_cursor_pos.col = 0;
|
|
|
|
m_cursor_pos.index = 0;
|
|
|
|
TTY_PRINT("{}", m_prompt);
|
2022-12-13 21:34:50 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2022-12-30 19:38:21 +02:00
|
|
|
case Input::Key::Escape:
|
2022-12-27 20:11:49 +02:00
|
|
|
TTY_PRINTLN("time since boot {} ms", PIT::ms_since_boot());
|
2022-12-19 11:35:24 +02:00
|
|
|
break;
|
|
|
|
|
2022-12-30 19:38:21 +02:00
|
|
|
case Input::Key::Tab:
|
2022-12-16 03:53:55 +02:00
|
|
|
break;
|
2022-12-13 21:34:50 +02:00
|
|
|
|
2023-01-13 15:07:24 +02:00
|
|
|
case Input::Key::Left:
|
|
|
|
if (m_cursor_pos.index > 0)
|
|
|
|
{
|
2023-02-01 21:05:44 +02:00
|
|
|
uint32_t len = get_last_length(current_buffer.sv().substring(0, m_cursor_pos.index));
|
2023-01-13 15:07:24 +02:00
|
|
|
m_cursor_pos.index -= len;
|
|
|
|
m_cursor_pos.col--;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Input::Key::Right:
|
2023-02-01 21:05:44 +02:00
|
|
|
if (m_cursor_pos.index < current_buffer.size())
|
2023-01-13 15:07:24 +02:00
|
|
|
{
|
2023-02-01 21:05:44 +02:00
|
|
|
uint32_t len = get_next_length(current_buffer.sv().substring(m_cursor_pos.index));
|
2023-01-13 15:07:24 +02:00
|
|
|
m_cursor_pos.index += len;
|
|
|
|
m_cursor_pos.col++;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Input::Key::Up:
|
|
|
|
if (m_cursor_pos.line > 0)
|
|
|
|
{
|
|
|
|
const auto& new_buffer = m_buffer[m_cursor_pos.line - 1];
|
|
|
|
m_cursor_pos.line--;
|
2023-02-01 21:05:44 +02:00
|
|
|
m_cursor_pos.index = new_buffer.size();
|
|
|
|
m_cursor_pos.col = get_unicode_character_count(new_buffer);
|
|
|
|
rerender_buffer();
|
2023-01-13 15:07:24 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Input::Key::Down:
|
2023-02-01 21:05:44 +02:00
|
|
|
if (m_cursor_pos.line < m_buffer.size() - 1)
|
2023-01-13 15:07:24 +02:00
|
|
|
{
|
|
|
|
const auto& new_buffer = m_buffer[m_cursor_pos.line + 1];
|
|
|
|
m_cursor_pos.line++;
|
2023-02-01 21:05:44 +02:00
|
|
|
m_cursor_pos.index = new_buffer.size();
|
|
|
|
m_cursor_pos.col = get_unicode_character_count(new_buffer);
|
|
|
|
rerender_buffer();
|
2023-01-13 15:07:24 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2022-12-13 21:34:50 +02:00
|
|
|
default:
|
|
|
|
{
|
2022-12-30 19:38:21 +02:00
|
|
|
const char* utf8 = Input::key_event_to_utf8(event);
|
2022-12-16 03:53:55 +02:00
|
|
|
if (utf8)
|
2022-12-13 21:34:50 +02:00
|
|
|
{
|
2023-02-01 21:05:44 +02:00
|
|
|
TTY_PRINT("{}{}", utf8, current_buffer.sv().substring(m_cursor_pos.index));
|
|
|
|
MUST(current_buffer.insert(utf8, m_cursor_pos.index));
|
2023-01-13 15:07:24 +02:00
|
|
|
m_cursor_pos.index += strlen(utf8);
|
|
|
|
m_cursor_pos.col++;
|
2022-12-13 21:34:50 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2022-12-30 20:48:15 +02:00
|
|
|
|
2023-01-13 15:07:24 +02:00
|
|
|
TTY_PRINT("\e[{}G", m_prompt_length + m_cursor_pos.col + 1);
|
2022-12-13 21:34:50 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|