Compare commits

..

73 Commits

Author SHA1 Message Date
8ab823fc43
wind: Move more fallible operations before window creation
All checks were successful
continuous-integration/drone/pr Build is passing
2023-09-02 14:02:06 +02:00
a2b6149f50
wind: Make sure stdin is always a TTY
All checks were successful
continuous-integration/drone/pr Build is passing
2023-08-31 14:55:40 +02:00
d5d21e1eb0
libui+wind+libos: Move shared memory handling code to os::SharedMemory
All checks were successful
continuous-integration/drone/pr Build is passing
2023-08-29 16:25:30 +02:00
e85dc97e91
libui: Add default handlers for events in Widget
All checks were successful
continuous-integration/drone/pr Build is passing
2023-08-29 15:32:33 +02:00
2c7ae3c61b
libui: Propagate Container events only if they are in the child widget's rect 2023-08-29 15:29:17 +02:00
f0f1a9c46c
libui+wind: Handle mouse leave events when the mouse leaves a window 2023-08-29 15:26:34 +02:00
1d4055411d
wind: Stop using the removed 'signal' pledge
All checks were successful
continuous-integration/drone/pr Build is passing
2023-08-28 12:51:16 +02:00
033b4c3db8
libui: Install the built library into the system root 2023-08-28 12:51:16 +02:00
744ca07799
wind: Show memory usage in debug output 2023-08-28 12:51:16 +02:00
8f0356700c
wind: Handle ftruncate() and mmap() errors properly 2023-08-28 12:51:16 +02:00
dad573f1fe
wind: Fix client references being out-of-date in windows when disconnecting other clients
Classic "keeping a pointer to an element inside a vector after the vector is updated" bug, ah yes.
2023-08-28 12:51:16 +02:00
870cb867ee
taskbar: Wait for terminated child windows 2023-08-28 12:51:16 +02:00
11d47c3982
wind: Add debug keybind 2023-08-28 12:51:16 +02:00
2eb973ca2b
wind+libos+libui: Handle interrupted reads properly 2023-08-28 12:51:16 +02:00
a227578107
base: Actually add the start icon to source control 2023-08-28 12:51:16 +02:00
8e28d45b9c
libui: Add Buttons 2023-08-28 12:51:15 +02:00
f8e6b45dd2
libui: Handle other mouse events 2023-08-28 12:51:15 +02:00
ff68f574ff
libui: Add aligned items using Containers, ImageWidget 2023-08-28 12:51:15 +02:00
da6daff8c7
libui: Add VerticalLayout 2023-08-28 12:51:15 +02:00
09cb378a7a
wind+libui+taskbar: Add GetScreenRect IPC, non-decorated windows, taskbar 2023-08-28 12:51:15 +02:00
d58a56a1d1
libui: Actually fill window backgrounds with the correct color 2023-08-28 12:51:15 +02:00
096a4e7953
libui: Add basic widget and layout system =D 2023-08-28 12:51:15 +02:00
9c30641587
ui+wind: Send mouse move events through IPC 2023-08-28 12:51:15 +02:00
75b3e78d34
wind+libui: Add protocol for window close requests 2023-08-28 12:51:15 +02:00
d801fe6bb8
libos+libui+wind: Use uppercase for static struct IDs to avoid confusion with fields 2023-08-28 12:51:14 +02:00
41f9d8cadd
libui+gclient: Add basic OOP wrappers around the IPC protocol 2023-08-28 12:51:14 +02:00
36cc66c2e9
wind+gclient: Add SetWindowTitle and support shm buffers 2023-08-28 12:51:14 +02:00
d6b75f5981
gclient: Create two example windows 2023-08-28 12:51:14 +02:00
c6bff6f8b4
wind: Handle CreateWindow IPC messages 2023-08-28 12:51:14 +02:00
03bde69c65
libui: Add CreateWindow IPC message definitions 2023-08-28 12:51:14 +02:00
9752ab29d3
libos: Add basic IPC message framework 2023-08-28 12:51:14 +02:00
2445c2f6aa
kernel: Fix poll syscall 2023-08-28 12:51:14 +02:00
7aaa05ded6
wind: Monitor data on client connections 2023-08-28 12:51:14 +02:00
3253151cb7
kernel: Add POLLHUP and store it when a polled socket's peer disconnects 2023-08-28 12:51:13 +02:00
e28f8f7520
libui: Add copyright/author text 2023-08-28 12:51:13 +02:00
65aa931931
libos: Add copyright/author comments to LocalServer and LocalClient 2023-08-28 12:51:13 +02:00
2192399ca2
wind: Use init --user and pledge() 2023-08-28 12:51:13 +02:00
0df32b92b3
Update .gitignore 2023-08-28 12:51:13 +02:00
baab157431
libos: Remove some shared pointers and change them to owned/live on the stack 2023-08-28 12:51:00 +02:00
0fcc5b9a5e
wind: Spawn a new client process after startup
Also, create the socket after dropping privileges.
2023-08-28 12:51:00 +02:00
92a4174f02
apps: Add gclient 2023-08-28 12:51:00 +02:00
48e97de59c
libos: Add os::LocalClient 2023-08-28 12:51:00 +02:00
f092e41247
libui: Change 'into' to 'onto' 2023-08-28 12:51:00 +02:00
c5ffbae6a0
libui: Document ui::Font 2023-08-28 12:51:00 +02:00
790d9d119b
libui+wind: Move some static variables inside functions 2023-08-28 12:50:59 +02:00
554a8ee300
wind: Generate random windows on keypresses 2023-08-28 12:50:59 +02:00
1caf80bde2
wind: Make sure windows have a minimum size to fit the titlebar 2023-08-28 12:50:59 +02:00
5b496791c5
libui: Properly cut off the last drawn character if necessary 2023-08-28 12:50:59 +02:00
fd99fc88ee
libui: Add Rect::contains(Rect) 2023-08-28 12:50:59 +02:00
4ac0adee57
libui: Render font characters properly with no spacing, matching the width calculations 2023-08-28 12:50:59 +02:00
432a5c27cb
wind: Render an actual TGA mouse cursor 2023-08-28 12:50:59 +02:00
8e01e2cd77
wind: Add a close button to windows using a TGA icon 2023-08-28 12:50:59 +02:00
ee7671a8cb
libui: Add support for TGA image loading 2023-08-28 12:50:59 +02:00
efa96655f3
libui: Add an interface to fill a Canvas with an array of pixels 2023-08-28 12:50:58 +02:00
a7c4e8900c
wind: Add window titlebars using ui::Font 2023-08-28 12:50:58 +02:00
f7763ca2bc
libui: Add PSF font loading and rendering 2023-08-28 12:50:56 +02:00
f104d491e0
libui: Add Color::GRAY 2023-08-28 12:50:29 +02:00
786bec0b18
libui: Rename Rect::absolute to normalized and add a new absolute function 2023-08-28 12:50:29 +02:00
57e1f49a32
libluna: Add assignment operators to Buffer 2023-08-28 12:50:20 +02:00
b49d9d55b7
wind: Reorder drag sequence 2023-08-28 12:49:45 +02:00
99080b8c50
libui: Add Rect::relative 2023-08-28 12:49:45 +02:00
9ecf259c71
libui: Remove redundant statement 2023-08-28 12:49:45 +02:00
dc107d14d7
libui: Add getters for separate color values 2023-08-28 12:49:45 +02:00
d39f0387fe
libui: Remove unnecessary stuff 2023-08-28 12:49:45 +02:00
1fa0bbfa37
base: Remove startup items not necessary for GUI startup 2023-08-28 12:49:45 +02:00
39296773d2
libui+wind: (Draggable) windows 2023-08-28 12:49:45 +02:00
d328c722f0
wind: Create a local server object 2023-08-28 12:49:45 +02:00
2177828456
libos: Add a new LocalServer class for local domain sockets 2023-08-28 12:49:45 +02:00
3252612d90
kernel: Support listening sockets in poll() 2023-08-28 12:49:44 +02:00
48a7ffcce8
base: Start wind on startup instead of the shell 2023-08-28 12:49:09 +02:00
57a05b73f3
wind: Add a simple display server skeleton using libui
No client functionality yet, but it's a start.
2023-08-28 12:49:09 +02:00
60dc10d4fc
libui: Add a GUI and graphics library 2023-08-28 12:49:09 +02:00
a8379b6c73
kernel: Fix negative movement in the PS/2 mouse driver 2023-08-28 12:49:06 +02:00
30 changed files with 352 additions and 955 deletions

View File

@ -261,7 +261,7 @@ static Result<void> load_service(const os::Path& path)
if (service.command.is_empty())
{
do_log("[init] service file is missing 'Command' entry, aborting!\n");
do_log("[init] service file is missing 'Command' or 'Script' entry, aborting!\n");
return {};
}

View File

@ -45,7 +45,7 @@ Result<int> luna_main(int argc, char** argv)
username = name.view();
}
execl("/usr/bin/su", "login", "-lp", "--", username.chars(), nullptr);
execl("/bin/su", "login", "-lp", "--", username.chars(), nullptr);
perror("su");
return 1;

View File

@ -8,7 +8,6 @@
#include <os/FileSystem.h>
#include <os/Mode.h>
#include <pwd.h>
#include <unistd.h>
void find_user_and_group(struct stat& st, StringView& owner, StringView& group)
{
@ -48,46 +47,18 @@ int sort_reverse(const os::Directory::Entry* a, const os::Directory::Entry* b)
return 0;
}
#define RESET_COLORS "\x1b[m"
#define SYMLINK_COLOR "\x1b[36m"
#define FILE_COLOR "\x1b[1;32m"
#define DIR_COLOR "\x1b[1;34m"
#define SOCKET_COLOR "\x1b[33m"
#define SPECIAL_COLOR "\x1b[35m"
#define STICKY_COLOR "\x1b[30;1;42m"
#define SETUID_COLOR "\x1b[30;1;41m"
#define EXEC_COLOR "\x1b[1;31m"
static const char* file_type_color(const os::Directory::Entry& entry)
{
if (entry.mode & S_ISVTX) return STICKY_COLOR;
if (entry.mode & S_ISUID || entry.mode & S_ISGID) return SETUID_COLOR;
switch (entry.mode & S_IFMT)
{
case S_IFREG: return entry.mode & S_IXUSR ? EXEC_COLOR : FILE_COLOR;
case S_IFDIR: return DIR_COLOR;
case S_IFLNK: return SYMLINK_COLOR;
case S_IFSOCK: return SOCKET_COLOR;
default: return SPECIAL_COLOR;
}
}
static Result<String> entry_join(const Vector<os::Directory::Entry>& vec, StringView delim, bool colors)
static Result<String> entry_join(const Vector<os::Directory::Entry>& vec, StringView delim)
{
if (vec.size() == 0) return String {};
if (vec.size() == 1) return vec[0].name.clone();
StringBuilder sb;
if (colors) TRY(sb.add(StringView { file_type_color(vec[0]) }));
TRY(sb.add(vec[0].name));
if (colors) TRY(sb.add(StringView { RESET_COLORS }));
for (usize i = 1; i < vec.size(); i++)
{
TRY(sb.add(delim));
if (colors) TRY(sb.add(StringView { file_type_color(vec[i]) }));
TRY(sb.add(vec[i].name));
if (colors) TRY(sb.add(StringView { RESET_COLORS }));
}
return sb.string();
@ -104,7 +75,6 @@ Result<int> luna_main(int argc, char** argv)
bool follow_symlink_args { false };
bool one_per_line { false };
bool list_directories { false };
bool no_colors { false };
StringView sort_type { "name" };
@ -128,8 +98,6 @@ Result<int> luna_main(int argc, char** argv)
parser.add_switch_argument(list_directories, 'd', "directory"_sv, "list directories instead of their contents"_sv);
parser.add_value_argument(sort_type, ' ', "sort"_sv, "sort by name, size or time"_sv);
parser.add_switch_argument(reverse_sort, 'r', "reverse"_sv, "reverse order while sorting"_sv);
parser.add_switch_argument(no_colors, ' ', "no-colors"_sv,
"disable coloring of output (defaults to true when not in a TTY)"_sv);
parser.parse(argc, argv);
Vector<os::Directory::Entry> files;
@ -186,13 +154,11 @@ Result<int> luna_main(int argc, char** argv)
if (!long_list)
{
auto list = TRY(entry_join(files, one_per_line ? "\n"_sv : " "_sv, !no_colors && isatty(STDIN_FILENO)));
auto list = TRY(entry_join(files, one_per_line ? "\n"_sv : " "_sv));
if (!list.is_empty()) os::println("%s", list.chars());
}
else
{
bool colors = !no_colors && isatty(STDIN_FILENO);
for (const auto& file : files)
{
struct stat st;
@ -210,32 +176,14 @@ Result<int> luna_main(int argc, char** argv)
if (!human_readable && !si)
{
if (colors)
{
os::println("%s %u %4s %4s %10lu %s%s" RESET_COLORS "%s" SYMLINK_COLOR "%s" RESET_COLORS,
formatted_mode, st.st_nlink, owner.chars(), group.chars(), st.st_size,
file_type_color(file), file.name.chars(), link.is_empty() ? "" : " -> ", link.chars());
}
else
{
os::println("%s %u %4s %4s %10lu %s%s%s", formatted_mode, st.st_nlink, owner.chars(), group.chars(),
st.st_size, file.name.chars(), link.is_empty() ? "" : " -> ", link.chars());
}
os::println("%s %u %4s %4s %10lu %s%s%s", formatted_mode, st.st_nlink, owner.chars(), group.chars(),
st.st_size, file.name.chars(), link.is_empty() ? "" : " -> ", link.chars());
}
else
{
auto size = TRY(to_dynamic_unit(st.st_size, 10, false, si ? Unit::SI : Unit::Binary, false));
if (colors)
{
os::println("%s %u %4s %4s %6s %s%s" RESET_COLORS "%s" SYMLINK_COLOR "%s" RESET_COLORS,
formatted_mode, st.st_nlink, owner.chars(), group.chars(), size.chars(),
file_type_color(file), file.name.chars(), link.is_empty() ? "" : " -> ", link.chars());
}
else
{
os::println("%s %u %4s %4s %6s %s%s%s", formatted_mode, st.st_nlink, owner.chars(), group.chars(),
size.chars(), file.name.chars(), link.is_empty() ? "" : " -> ", link.chars());
}
os::println("%s %u %4s %4s %6s %s%s%s", formatted_mode, st.st_nlink, owner.chars(), group.chars(),
size.chars(), file.name.chars(), link.is_empty() ? "" : " -> ", link.chars());
}
}
}

View File

@ -72,9 +72,10 @@ int main()
// Now, mount the /dev file system on the new root.
mount_devfs();
char* argv[] = { "/usr/bin/init", nullptr };
setenv("PATH", "/sbin:/usr/bin", 1);
char* argv[] = { "init", nullptr };
char* envp[] = { nullptr };
execve(argv[0], argv, envp);
execvpe(argv[0], argv, envp);
fail_errno("Failed to execute init");

View File

@ -1,35 +1,10 @@
#include <os/ArgumentParser.h>
#include <os/Directory.h>
#include <os/File.h>
#include <os/FileSystem.h>
Result<void> remove_wrapper(const os::Path& path, bool verbose)
{
TRY(os::FileSystem::remove(path));
if (verbose) os::println("removed '%s'", path.name().chars());
return {};
}
Result<void> remove_tree(const os::Path& path, bool verbose)
{
auto rc = remove_wrapper(path, verbose);
if (!rc.has_error()) return {};
if (rc.error() != ENOTEMPTY) return rc.release_error();
auto dir = TRY(os::Directory::open(path));
Vector<String> entries = TRY(dir->list_names(os::Directory::Filter::ParentAndBase));
for (const auto& entry : entries) { TRY(remove_tree({ dir->fd(), entry.view() }, verbose)); }
return remove_wrapper(path, verbose);
}
Result<int> luna_main(int argc, char** argv)
{
StringView path;
bool recursive;
bool verbose;
os::ArgumentParser parser;
parser.add_description("Remove a path from the file system."_sv);
@ -37,12 +12,11 @@ Result<int> luna_main(int argc, char** argv)
parser.add_positional_argument(path, "path"_sv, true);
parser.add_switch_argument(recursive, 'r', "recursive"_sv,
"remove a directory recursively (by default, rm removes only empty directories)"_sv);
parser.add_switch_argument(verbose, 'v', "verbose"_sv, "log every removed file and directory"_sv);
parser.parse(argc, argv);
if (!recursive) TRY(remove_wrapper(path, verbose));
if (!recursive) TRY(os::FileSystem::remove(path));
else
TRY(remove_tree(path, verbose));
TRY(os::FileSystem::remove_tree(path));
return 0;
}

View File

@ -127,7 +127,7 @@ Result<int> luna_main(int argc, char** argv)
{
chdir(entry->pw_dir);
clearenv();
setenv("PATH", "/usr/bin:/usr/local/bin", 1);
setenv("PATH", "/bin:/sbin", 1);
setpgid(0, 0);
}

View File

@ -1,2 +1,2 @@
root:toor:0:0:Administrator:/:/usr/bin/sh
selene:moon:1000:1000:User:/home/selene:/usr/bin/sh
root:toor:0:0:Administrator:/:/bin/sh
selene:moon:1000:1000:User:/home/selene:/bin/sh

View File

@ -161,11 +161,7 @@ void io_thread()
static void timer_interrupt(Registers* regs, void*)
{
Timer::tick();
if (should_invoke_scheduler())
{
Scheduler::invoke(regs);
TextConsole::tick_cursor();
}
if (should_invoke_scheduler()) Scheduler::invoke(regs);
}
static void keyboard_interrupt(Registers*, void*)

View File

@ -246,9 +246,6 @@ Result<u64> ConsoleDevice::ioctl(int request, void* arg)
}
case TTYSETGFX: {
s_is_in_graphical_mode = (bool)arg;
if (!s_is_in_graphical_mode) TextConsole::enable_cursor();
else
TextConsole::disable_cursor();
return 0;
}
default: return err(EINVAL);

View File

@ -59,8 +59,6 @@ Result<u64> sys_execve(Registers* regs, SyscallArgs args)
Vector<String> envp;
if (args[2]) envp = TRY(copy_string_vector_from_userspace(args[2]));
String cmdline = TRY(String::join(argv, " "));
if ((calculate_userspace_stack_size(argv) + calculate_userspace_stack_size(envp)) > MAX_ARGV_STACK_SIZE)
return err(E2BIG);
@ -117,7 +115,7 @@ Result<u64> sys_execve(Registers* regs, SyscallArgs args)
if (is_setuid) current->auth.euid = current->auth.suid = inode->metadata().uid;
if (is_setgid) current->auth.egid = current->auth.sgid = inode->metadata().gid;
current->cmdline = cmdline.chars();
current->name = path.chars();
image->apply(current);
@ -161,7 +159,7 @@ Result<u64> sys_fork(Registers* regs, SyscallArgs)
thread->state = ThreadState::Runnable;
thread->is_kernel = false;
thread->fp_data.save();
thread->cmdline = current->cmdline;
thread->name = current->name;
thread->auth = current->auth;
thread->current_directory = current->current_directory;
thread->current_directory_path = move(current_directory_path);

View File

@ -35,7 +35,7 @@ Result<u64> sys_pstat(Registers*, SyscallArgs args)
set_timespec(proc.ps_time, thread->user_ticks_self + thread->kernel_ticks_self);
set_timespec(proc.ps_ktime, thread->kernel_ticks_self);
set_timespec(proc.ps_utime, thread->kernel_ticks_children);
strlcpy(proc.ps_name, thread->cmdline.chars(), sizeof(proc.ps_name));
strlcpy(proc.ps_name, thread->name.chars(), sizeof(proc.ps_name));
strlcpy(proc.ps_cwd, thread->current_directory_path.is_empty() ? "/" : thread->current_directory_path.chars(),
sizeof(proc.ps_cwd));

View File

@ -27,7 +27,7 @@ namespace Scheduler
g_idle.state = ThreadState::Idle;
g_idle.is_kernel = true;
g_idle.parent = nullptr;
g_idle.cmdline = "[idle]";
g_idle.name = "[idle]";
g_idle.active_directory = nullptr;
g_idle.ticks_left = 1;
@ -96,7 +96,7 @@ namespace Scheduler
thread->stack = thread_stack;
thread->cmdline = name;
thread->name = name;
thread->is_kernel = true;
thread->active_directory = MMU::kernel_page_directory();
@ -148,7 +148,7 @@ namespace Scheduler
thread->is_kernel = false;
thread->id = 1;
thread->pgid = 1;
thread->cmdline = name;
thread->name = name;
thread->auth = Credentials { .uid = 0, .euid = 0, .suid = 0, .gid = 0, .egid = 0, .sgid = 0 };
Vector<String> args;
@ -374,7 +374,7 @@ namespace Scheduler
{
kdbgln("%p %c [%-20s] %4d, parent = (%-18p,%d), state = %d, ticks: (k:%04zu,u:%04zu), status = "
"%d, cwd = %s",
thread, thread->is_kernel ? 'k' : 'u', thread->cmdline.chars(), thread->id, thread->parent,
thread, thread->is_kernel ? 'k' : 'u', thread->name.chars(), thread->id, thread->parent,
thread->parent ? thread->parent->id : 0, (int)thread->state, thread->kernel_ticks_self,
thread->user_ticks_self, thread->status,
thread->current_directory_path.is_empty() ? "/" : thread->current_directory_path.chars());

View File

@ -123,7 +123,7 @@ struct Thread : public LinkedListNode<Thread>
mode_t umask { 0 };
StaticString<128> cmdline;
StaticString<128> name;
String current_directory_path = {};
SharedPtr<VFS::Inode> current_directory = {};

View File

@ -3,7 +3,6 @@
#include "video/Framebuffer.h"
#include <luna/CString.h>
#include <luna/CType.h>
#include <luna/EscapeSequence.h>
#include <luna/Format.h>
#include <luna/Result.h>
#include <luna/ScopeGuard.h>
@ -14,27 +13,8 @@ extern const BOOTBOOT bootboot;
#include "video/BuiltinFont.h"
// Default text color.
static constexpr u32 WHITE = 0xffffffff;
// xterm color palette.
static constexpr u32 BLACK = 0xff000000;
static constexpr u32 RED = 0xffcd0000;
static constexpr u32 GREEN = 0xff00cd00;
static constexpr u32 YELLOW = 0xffcdcd00;
static constexpr u32 BLUE = 0xff0000ee;
static constexpr u32 MAGENTA = 0xffcd00cd;
static constexpr u32 CYAN = 0xff00cdcd;
static constexpr u32 GRAY = 0xffe5e5e5;
static constexpr u32 BRIGHT_BLACK = 0xff7f7f7f;
static constexpr u32 BRIGHT_RED = 0xffff0000;
static constexpr u32 BRIGHT_GREEN = 0xff00ff00;
static constexpr u32 BRIGHT_YELLOW = 0xffffff00;
static constexpr u32 BRIGHT_BLUE = 0xff5c5cff;
static constexpr u32 BRIGHT_MAGENTA = 0xffff00ff;
static constexpr u32 BRIGHT_CYAN = 0xff00ffff;
static constexpr u32 BRIGHT_GRAY = 0xffffffff;
static constexpr u32 WHITE = 0xffffffff;
static u32 g_background_color = BLACK;
static u32 g_foreground_color = WHITE;
@ -42,20 +22,11 @@ static u32 g_foreground_color = WHITE;
static constexpr u32 FONT_HEIGHT = 16;
static constexpr u32 FONT_WIDTH = 8;
static bool bold = false;
static u32 g_x_position = 0;
static u32 g_y_position = 0;
static constexpr int CURSOR_TIMEOUT = 500;
static int current_cursor_timeout = CURSOR_TIMEOUT;
static bool cursor_activated = true;
static bool cursor_enabled = true;
static Utf8StateDecoder utf8_decoder;
static Option<EscapeSequenceParser> escape_sequence_parser;
static void putwchar_at(wchar_t c, u32 x, u32 y)
{
const u8* glyph = &font[c * 16];
@ -87,7 +58,7 @@ static void scroll()
static bool should_scroll()
{
return g_y_position >= Framebuffer::height();
return (g_y_position + FONT_HEIGHT) >= Framebuffer::height();
}
static void next_line()
@ -111,216 +82,11 @@ static void erase_current_char()
Framebuffer::rect(g_x_position, g_y_position, FONT_WIDTH, FONT_HEIGHT, BLACK);
}
static void draw_cursor()
{
Framebuffer::rect(g_x_position, g_y_position, FONT_WIDTH, FONT_HEIGHT, WHITE);
}
static bool at_end_of_screen()
{
return (g_x_position + FONT_WIDTH) > Framebuffer::width();
}
static bool handle_escape_sequence(wchar_t c)
{
auto rc = escape_sequence_parser->advance(static_cast<u8>(c));
if (rc.has_error())
{
escape_sequence_parser = Option<EscapeSequenceParser> {};
return false;
}
if (!rc.value()) return true;
if (!escape_sequence_parser->valid())
{
escape_sequence_parser = Option<EscapeSequenceParser> {};
return false;
}
const auto& params = escape_sequence_parser->parameters();
switch (escape_sequence_parser->code())
{
case EscapeCode::CursorUp: {
int lines = params.size() ? params[0] : 1;
int pixels = lines * FONT_HEIGHT;
if ((u32)pixels > g_y_position) g_y_position = 0;
else
g_y_position -= pixels;
};
break;
case EscapeCode::CursorDown: {
int lines = params.size() ? params[0] : 1;
int pixels = lines * FONT_HEIGHT;
if (pixels + g_y_position >= Framebuffer::height()) g_y_position = Framebuffer::height() - FONT_HEIGHT;
else
g_y_position += pixels;
};
break;
case EscapeCode::CursorBack: {
int chars = params.size() ? params[0] : 1;
int pixels = chars * FONT_WIDTH;
if ((u32)pixels > g_x_position) g_x_position = 0;
else
g_x_position -= pixels;
};
break;
case EscapeCode::CursorForward: {
int chars = params.size() ? params[0] : 1;
int pixels = chars * FONT_WIDTH;
if (pixels + g_x_position >= Framebuffer::width()) g_x_position = Framebuffer::width() - FONT_WIDTH;
else
g_x_position += pixels;
};
break;
case EscapeCode::CursorNextLine: {
int lines = params.size() ? params[0] : 1;
int pixels = lines * FONT_HEIGHT;
if ((u32)pixels > g_y_position) g_y_position = 0;
else
g_y_position -= pixels;
g_x_position = 0;
};
break;
case EscapeCode::CursorPreviousLine: {
int lines = params.size() ? params[0] : 1;
int pixels = lines * FONT_HEIGHT;
if (pixels + g_y_position >= Framebuffer::height()) g_y_position = Framebuffer::height() - FONT_HEIGHT;
else
g_y_position += pixels;
g_x_position = 0;
};
break;
case EscapeCode::CursorHorizontalAbsolute: {
int line = (params.size() ? params[0] : 1) - 1;
if (line < 0) break;
u32 position = line * FONT_HEIGHT;
if (position >= Framebuffer::height()) position = Framebuffer::height() - FONT_HEIGHT;
g_y_position = position;
};
break;
case EscapeCode::SetCursorPosition: {
int x = (params.size() ? params[0] : 1) - 1;
int y = (params.size() > 1 ? params[1] : 1) - 1;
if (x < 0 || y < 0) break;
u32 x_position = x * FONT_WIDTH;
if (x_position >= Framebuffer::width()) x_position = Framebuffer::width() - FONT_HEIGHT;
g_x_position = x_position;
u32 y_position = y * FONT_HEIGHT;
if (y_position >= Framebuffer::height()) y_position = Framebuffer::height() - FONT_HEIGHT;
g_y_position = y_position;
};
break;
case EscapeCode::SelectGraphicRendition: {
if (!params.size())
{
g_foreground_color = WHITE;
g_background_color = BLACK;
bold = false;
break;
}
for (usize i = 0; i < params.size(); i++)
{
int arg = params[i];
switch (arg)
{
case 0: {
g_foreground_color = BLACK;
g_background_color = WHITE;
bold = false;
break;
}
case 1: {
bold = true;
break;
}
case 22: {
bold = false;
break;
}
case 30: {
g_foreground_color = bold ? BRIGHT_BLACK : BLACK;
break;
}
case 31: {
g_foreground_color = bold ? BRIGHT_RED : RED;
break;
}
case 32: {
g_foreground_color = bold ? BRIGHT_GREEN : GREEN;
break;
}
case 33: {
g_foreground_color = bold ? BRIGHT_YELLOW : YELLOW;
break;
}
case 34: {
g_foreground_color = bold ? BRIGHT_BLUE : BLUE;
break;
}
case 35: {
g_foreground_color = bold ? BRIGHT_MAGENTA : MAGENTA;
break;
}
case 36: {
g_foreground_color = bold ? BRIGHT_CYAN : CYAN;
break;
}
case 37: {
g_foreground_color = bold ? BRIGHT_GRAY : GRAY;
break;
}
case 39: {
g_foreground_color = WHITE;
break;
}
case 40: {
g_background_color = bold ? BRIGHT_BLACK : BLACK;
break;
}
case 41: {
g_background_color = bold ? BRIGHT_RED : RED;
break;
}
case 42: {
g_background_color = bold ? BRIGHT_GREEN : GREEN;
break;
}
case 43: {
g_background_color = bold ? BRIGHT_YELLOW : YELLOW;
break;
}
case 44: {
g_background_color = bold ? BRIGHT_BLUE : BLUE;
break;
}
case 45: {
g_background_color = bold ? BRIGHT_MAGENTA : MAGENTA;
break;
}
case 46: {
g_background_color = bold ? BRIGHT_CYAN : CYAN;
break;
}
case 47: {
g_background_color = bold ? BRIGHT_GRAY : GRAY;
break;
}
case 49: {
g_background_color = BLACK;
break;
}
default: break;
}
}
}
break;
default: break;
}
escape_sequence_parser = Option<EscapeSequenceParser> {};
return true;
}
namespace TextConsole
{
void putwchar(wchar_t c)
@ -328,16 +94,6 @@ namespace TextConsole
// Unprintable (not in the built-in font) characters get represented as a box
if (c > (wchar_t)255) c = (wchar_t)256;
if (escape_sequence_parser.has_value())
{
if (handle_escape_sequence(c)) return;
}
// Erase the current cursor.
if (cursor_enabled) erase_current_char();
bool should_draw_cursor = cursor_enabled;
switch (c)
{
case L'\n': {
@ -357,13 +113,6 @@ namespace TextConsole
erase_current_char();
}
break;
case L'\x1b':
case L'\x9b':
case L'\x90':
case L'\x9d':
escape_sequence_parser = EscapeSequenceParser { (u8)c };
should_draw_cursor = false;
break;
default: {
if (_iscntrl(c)) return;
putwchar_at(c, g_x_position, g_y_position);
@ -376,42 +125,6 @@ namespace TextConsole
break;
}
}
if (should_draw_cursor)
{
current_cursor_timeout = CURSOR_TIMEOUT;
cursor_activated = true;
draw_cursor();
}
}
void tick_cursor()
{
if (!cursor_enabled) return;
current_cursor_timeout--;
if (current_cursor_timeout == 0)
{
current_cursor_timeout = CURSOR_TIMEOUT;
cursor_activated = !cursor_activated;
if (cursor_activated) draw_cursor();
else
erase_current_char();
}
}
void disable_cursor()
{
cursor_enabled = false;
}
void enable_cursor()
{
cursor_enabled = true;
cursor_activated = true;
current_cursor_timeout = CURSOR_TIMEOUT;
draw_cursor();
}
Result<void> putchar(char c)

View File

@ -19,9 +19,6 @@ namespace TextConsole
Result<void> println(const char* str);
void wprintln(const wchar_t* str);
Result<usize> printf(const char* format, ...) _format(1, 2);
void tick_cursor();
void disable_cursor();
void enable_cursor();
u16 rows();
u16 cols();

View File

@ -20,6 +20,7 @@ set(SOURCES
src/pwd.cpp
src/grp.cpp
src/locale.cpp
src/scanf.cpp
src/signal.cpp
src/termios.cpp
src/utime.cpp

263
libc/src/scanf.cpp Normal file
View File

@ -0,0 +1,263 @@
#include <errno.h>
#include <luna/CType.h>
#include <luna/NumberParsing.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FLAG_DISCARD (1 << 0)
#define FLAG_ALLOC (1 << 1)
#define FLAG_WIDTH (1 << 2)
#define FLAG_LONG (1 << 3)
#define FLAG_LONG_LONG (1 << 4)
#define FLAG_SHORT (1 << 5)
#define FLAG_CHAR (1 << 6)
static int parse_flags(const char** format)
{
int result = 0;
while (true)
{
switch (**format)
{
case '*':
result |= FLAG_DISCARD;
(*format)++;
break;
case 'm':
result |= FLAG_ALLOC;
(*format)++;
break;
default: return result;
}
}
}
static size_t parse_width(const char** format, int& flags)
{
size_t result = 0;
if (_isdigit(**format))
{
result = scan_unsigned_integer(format);
flags |= FLAG_WIDTH;
}
return result;
}
static void parse_type(const char** format, int& flags)
{
// FIXME: Support %j (intmax_t/uintmax_t)
switch (**format)
{
case 'h':
flags |= FLAG_SHORT;
(*format)++;
if (**format == 'h')
{
flags |= FLAG_CHAR;
(*format)++;
}
break;
case 'l':
flags |= FLAG_LONG;
(*format)++;
if (**format == 'l')
{
flags |= FLAG_LONG_LONG;
(*format)++;
}
break;
case 't':
flags |= (sizeof(ptrdiff_t) == sizeof(long)) ? FLAG_LONG : FLAG_LONG_LONG;
(*format)++;
break;
case 'z':
flags |= (sizeof(size_t) == sizeof(long)) ? FLAG_LONG : FLAG_LONG_LONG;
(*format)++;
break;
default: break;
}
}
static void write_parsed_signed_integer(ssize_t value, int flags, va_list ap)
{
if (flags & FLAG_LONG_LONG) *va_arg(ap, signed long long*) = (signed long long)value;
else if (flags & FLAG_LONG)
*va_arg(ap, signed long*) = (signed long)value;
else if (flags & FLAG_SHORT)
*va_arg(ap, signed int*) = (signed short)value;
else if (flags & FLAG_CHAR)
*va_arg(ap, signed int*) = (signed char)value;
else
*va_arg(ap, signed int*) = (signed int)value;
}
static void write_parsed_unsigned_integer(size_t value, int flags, va_list ap)
{
if (flags & FLAG_LONG_LONG) *va_arg(ap, unsigned long long*) = (unsigned long long)value;
else if (flags & FLAG_LONG)
*va_arg(ap, unsigned long*) = (unsigned long)value;
else if (flags & FLAG_SHORT)
*va_arg(ap, unsigned int*) = (unsigned short)value;
else if (flags & FLAG_CHAR)
*va_arg(ap, unsigned int*) = (unsigned char)value;
else
*va_arg(ap, unsigned int*) = (unsigned int)value;
}
#define WHITESPACE_CHARACTERS " \t\f\r\n\v"
static void skip_whitespace(const char** str)
{
*str += strspn(*str, WHITESPACE_CHARACTERS);
}
extern "C"
{
int vsscanf(const char* str, const char* format, va_list ap)
{
int parsed = 0;
const char* s = str; // Keep a pointer to the beginning of the string for %n
if (*str == 0) return EOF;
while (*format)
{
if (*format != '%')
{
normal:
if (!_isspace(*format))
{
if (*str != *format) return parsed;
str++;
format++;
if (*str == 0) return parsed;
continue;
}
skip_whitespace(&format);
skip_whitespace(&str);
if (*str == 0) return parsed;
continue;
}
else
{
format++;
if (*format == '%')
{
skip_whitespace(&str);
goto normal;
}
int flags = parse_flags(&format);
size_t width = parse_width(&format, flags);
parse_type(&format, flags);
char specifier = *format++;
if (!specifier) return parsed;
switch (specifier)
{
case 's': {
skip_whitespace(&str);
size_t chars = strcspn(str, WHITESPACE_CHARACTERS);
if (!chars) return parsed;
if ((flags & FLAG_WIDTH) && chars > width) chars = width;
if (!(flags & FLAG_DISCARD))
{
char* ptr;
if (flags & FLAG_ALLOC)
{
ptr = (char*)malloc(chars + 1);
if (!ptr) return parsed;
*va_arg(ap, char**) = ptr;
}
else
ptr = va_arg(ap, char*);
memcpy(ptr, str, chars);
ptr[chars] = 0;
}
str += chars;
parsed++;
break;
}
case 'c': {
if (strlen(str) < width) return parsed;
if (!(flags & FLAG_WIDTH)) width = 1;
if (!(flags & FLAG_DISCARD))
{
char* ptr;
if (flags & FLAG_ALLOC)
{
ptr = (char*)malloc(width);
if (!ptr) return parsed;
*va_arg(ap, char**) = ptr;
}
else
ptr = va_arg(ap, char*);
memcpy(ptr, str, width);
}
str += width;
parsed++;
break;
}
case 'd': {
skip_whitespace(&str);
ssize_t value = scan_signed_integer(&str, 10);
if (!(flags & FLAG_DISCARD)) write_parsed_signed_integer(value, flags, ap);
parsed++;
break;
}
case 'i': {
skip_whitespace(&str);
ssize_t value = scan_signed_integer(&str, 0);
if (!(flags & FLAG_DISCARD)) write_parsed_signed_integer(value, flags, ap);
parsed++;
break;
}
case 'o': {
skip_whitespace(&str);
size_t value = scan_unsigned_integer(&str, 8);
if (!(flags & FLAG_DISCARD)) write_parsed_unsigned_integer(value, flags, ap);
parsed++;
break;
}
case 'u': {
skip_whitespace(&str);
size_t value = scan_unsigned_integer(&str, 10);
if (!(flags & FLAG_DISCARD)) write_parsed_unsigned_integer(value, flags, ap);
parsed++;
break;
}
case 'X':
case 'x': {
skip_whitespace(&str);
size_t value = scan_unsigned_integer(&str, 16);
if (!(flags & FLAG_DISCARD)) write_parsed_unsigned_integer(value, flags, ap);
parsed++;
break;
}
case 'p': {
skip_whitespace(&str);
size_t value = scan_unsigned_integer(&str, 16);
if (!(flags & FLAG_DISCARD)) *va_arg(ap, void**) = (void*)value;
parsed++;
break;
}
case 'n': {
if (!(flags & FLAG_DISCARD)) *va_arg(ap, int*) = (int)(str - s);
break;
}
default: {
fprintf(stderr, "vsscanf: unknown conversion specifier: %%%c\n", specifier);
return parsed;
}
}
}
}
return parsed;
}
}

View File

@ -2,7 +2,6 @@
#include <fcntl.h>
#include <luna/Common.h>
#include <luna/Format.h>
#include <luna/Scanf.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -619,17 +618,12 @@ extern "C"
return rc;
}
int vsscanf(const char* str, const char* format, va_list ap)
{
return scanf_impl(str, format, ap);
}
int sscanf(const char* str, const char* format, ...)
{
va_list ap;
va_start(ap, format);
int rc = scanf_impl(str, format, ap);
int rc = vsscanf(str, format, ap);
va_end(ap);
@ -640,7 +634,7 @@ extern "C"
{
char buf[BUFSIZ];
if (!fgets(buf, sizeof(buf), stream)) return EOF;
return scanf_impl(buf, format, ap);
return vsscanf(buf, format, ap);
}
int fscanf(FILE* stream, const char* format, ...)

View File

@ -23,7 +23,7 @@ static Result<int> try_execvpe(const char* name, char* const* argv, char* const*
if (strchr(name, '/')) return execve(name, argv, envp);
char* path = getenv("PATH");
if (!path) path = const_cast<char*>("/usr/bin:/usr/local/bin");
if (!path) path = const_cast<char*>("/bin:/sbin");
Vector<String> paths = TRY(StringView { path }.split(":"));

View File

@ -5,7 +5,6 @@ file(GLOB HEADERS include/luna/*.h)
set(FREESTANDING_SOURCES
${HEADERS}
src/CRC32.cpp
src/EscapeSequence.cpp
src/Format.cpp
src/Sort.cpp
src/NumberParsing.cpp
@ -15,7 +14,6 @@ set(FREESTANDING_SOURCES
src/SystemError.cpp
src/Bitmap.cpp
src/Buffer.cpp
src/Scanf.cpp
src/Stack.cpp
src/String.cpp
src/StringBuilder.cpp

View File

@ -1,67 +0,0 @@
/**
* @file EscapeSequence.h
* @author apio (cloudapio.eu)
* @brief ANSI escape sequence parsing.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#pragma once
#include <luna/Vector.h>
enum class EscapeCode
{
SaveCursor,
RestoreCursor,
CursorUp,
CursorDown,
CursorForward,
CursorBack,
CursorNextLine,
CursorPreviousLine,
CursorHorizontalAbsolute,
SetCursorPosition,
SelectGraphicRendition,
};
class EscapeSequenceParser
{
public:
EscapeSequenceParser(u8 begin);
Result<bool> advance(u8 byte);
bool valid() const
{
return m_valid;
}
const Vector<int>& parameters() const
{
return m_parameters;
}
EscapeCode code() const
{
return m_escape_code;
}
private:
enum class SequenceType
{
ESC,
CSI,
DCS,
OSC,
};
Vector<u8> m_parameter;
Vector<int> m_parameters;
SequenceType m_sequence_type;
bool m_parsing_parameter { false };
bool m_valid { false };
EscapeCode m_escape_code;
};

View File

@ -1,21 +0,0 @@
/**
* @file Scanf.h
* @author apio (cloudapio.eu)
* @brief Scanf implementation.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#pragma once
#include <stdarg.h>
/**
* @brief scanf() implementation.
*
* @param str The string to read input from.
* @param format The format string.
* @param ap The variadic argument list.
* @return int The number of arguments read, or -1 if the string was empty.
*/
int scanf_impl(const char* str, const char* format, va_list ap);

View File

@ -1,146 +0,0 @@
/**
* @file EscapeSequence.cpp
* @author apio (cloudapio.eu)
* @brief ANSI escape sequence parsing.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#include <luna/CType.h>
#include <luna/Check.h>
#include <luna/EscapeSequence.h>
#include <luna/NumberParsing.h>
EscapeSequenceParser::EscapeSequenceParser(u8 begin)
{
switch (begin)
{
case 0x1b: m_sequence_type = SequenceType::ESC; break;
case 0x9b: m_sequence_type = SequenceType::CSI; break;
case 0x90: m_sequence_type = SequenceType::DCS; break;
case 0x9d: m_sequence_type = SequenceType::OSC; break;
default: fail("Unrecognized escape sequence type");
}
}
Result<bool> EscapeSequenceParser::advance(u8 byte)
{
switch (m_sequence_type)
{
case SequenceType::ESC: {
switch (byte)
{
case '[': {
m_sequence_type = SequenceType::CSI;
return false;
};
case 'P': {
m_sequence_type = SequenceType::DCS;
return false;
};
case ']': {
m_sequence_type = SequenceType::OSC;
return false;
};
case '7': {
m_escape_code = EscapeCode::SaveCursor;
m_valid = true;
return true;
};
case '8': {
m_escape_code = EscapeCode::RestoreCursor;
m_valid = true;
return true;
};
default: {
m_valid = false;
return true;
}
}
};
break;
case SequenceType::CSI: {
if (_isdigit(byte))
{
m_parsing_parameter = true;
TRY(m_parameter.try_append(byte));
return false;
}
if (!m_parsing_parameter && byte == ';')
{
TRY(m_parameters.try_append(0));
return false;
}
if (m_parsing_parameter)
{
TRY(m_parameter.try_append(0));
int value = static_cast<int>(parse_unsigned_integer((const char*)m_parameter.data(), nullptr, 10));
m_parameter.clear();
TRY(m_parameters.try_append(value));
}
switch (byte)
{
case 'A': {
m_escape_code = EscapeCode::CursorUp;
m_valid = true;
return true;
};
case 'B': {
m_escape_code = EscapeCode::CursorDown;
m_valid = true;
return true;
};
case 'C': {
m_escape_code = EscapeCode::CursorForward;
m_valid = true;
return true;
};
case 'D': {
m_escape_code = EscapeCode::CursorBack;
m_valid = true;
return true;
};
case 'E': {
m_escape_code = EscapeCode::CursorNextLine;
m_valid = true;
return true;
};
case 'F': {
m_escape_code = EscapeCode::CursorPreviousLine;
m_valid = true;
return true;
};
case 'G': {
m_escape_code = EscapeCode::CursorHorizontalAbsolute;
m_valid = true;
return true;
};
case 'H': {
m_escape_code = EscapeCode::SetCursorPosition;
m_valid = true;
return true;
};
case 'm': {
m_escape_code = EscapeCode::SelectGraphicRendition;
m_valid = true;
return true;
};
case ';': {
return false;
};
default: {
m_valid = false;
return true;
}
}
};
break;
case SequenceType::DCS: todo();
case SequenceType::OSC: todo();
default: todo();
}
}

View File

@ -1,270 +0,0 @@
/**
* @file Scanf.cpp
* @author apio (cloudapio.eu)
* @brief Scanf implementation.
*
* @copyright Copyright (c) 2023, the Luna authors.
*
*/
#include <luna/CString.h>
#include <luna/CType.h>
#include <luna/DebugLog.h>
#include <luna/Heap.h>
#include <luna/NumberParsing.h>
#include <luna/SystemError.h>
#include <sys/types.h>
#define FLAG_DISCARD (1 << 0)
#define FLAG_ALLOC (1 << 1)
#define FLAG_WIDTH (1 << 2)
#define FLAG_LONG (1 << 3)
#define FLAG_LONG_LONG (1 << 4)
#define FLAG_SHORT (1 << 5)
#define FLAG_CHAR (1 << 6)
static int parse_flags(const char** format)
{
int result = 0;
while (true)
{
switch (**format)
{
case '*':
result |= FLAG_DISCARD;
(*format)++;
break;
case 'm':
result |= FLAG_ALLOC;
(*format)++;
break;
default: return result;
}
}
}
static size_t parse_width(const char** format, int& flags)
{
size_t result = 0;
if (_isdigit(**format))
{
result = scan_unsigned_integer(format);
flags |= FLAG_WIDTH;
}
return result;
}
static void parse_type(const char** format, int& flags)
{
// FIXME: Support %j (intmax_t/uintmax_t)
switch (**format)
{
case 'h':
flags |= FLAG_SHORT;
(*format)++;
if (**format == 'h')
{
flags |= FLAG_CHAR;
(*format)++;
}
break;
case 'l':
flags |= FLAG_LONG;
(*format)++;
if (**format == 'l')
{
flags |= FLAG_LONG_LONG;
(*format)++;
}
break;
case 't':
flags |= (sizeof(ptrdiff_t) == sizeof(long)) ? FLAG_LONG : FLAG_LONG_LONG;
(*format)++;
break;
case 'z':
flags |= (sizeof(size_t) == sizeof(long)) ? FLAG_LONG : FLAG_LONG_LONG;
(*format)++;
break;
default: break;
}
}
static void write_parsed_signed_integer(ssize_t value, int flags, va_list ap)
{
if (flags & FLAG_LONG_LONG) *va_arg(ap, signed long long*) = (signed long long)value;
else if (flags & FLAG_LONG)
*va_arg(ap, signed long*) = (signed long)value;
else if (flags & FLAG_SHORT)
*va_arg(ap, signed int*) = (signed short)value;
else if (flags & FLAG_CHAR)
*va_arg(ap, signed int*) = (signed char)value;
else
*va_arg(ap, signed int*) = (signed int)value;
}
static void write_parsed_unsigned_integer(size_t value, int flags, va_list ap)
{
if (flags & FLAG_LONG_LONG) *va_arg(ap, unsigned long long*) = (unsigned long long)value;
else if (flags & FLAG_LONG)
*va_arg(ap, unsigned long*) = (unsigned long)value;
else if (flags & FLAG_SHORT)
*va_arg(ap, unsigned int*) = (unsigned short)value;
else if (flags & FLAG_CHAR)
*va_arg(ap, unsigned int*) = (unsigned char)value;
else
*va_arg(ap, unsigned int*) = (unsigned int)value;
}
#define WHITESPACE_CHARACTERS " \t\f\r\n\v"
static void skip_whitespace(const char** str)
{
*str += strspn(*str, WHITESPACE_CHARACTERS);
}
int scanf_impl(const char* str, const char* format, va_list ap)
{
int parsed = 0;
const char* s = str; // Keep a pointer to the beginning of the string for %n
if (*str == 0) return -1;
while (*format)
{
if (*format != '%')
{
normal:
if (!_isspace(*format))
{
if (*str != *format) return parsed;
str++;
format++;
if (*str == 0) return parsed;
continue;
}
skip_whitespace(&format);
skip_whitespace(&str);
if (*str == 0) return parsed;
continue;
}
else
{
format++;
if (*format == '%')
{
skip_whitespace(&str);
goto normal;
}
int flags = parse_flags(&format);
size_t width = parse_width(&format, flags);
parse_type(&format, flags);
char specifier = *format++;
if (!specifier) return parsed;
switch (specifier)
{
case 's': {
skip_whitespace(&str);
size_t chars = strcspn(str, WHITESPACE_CHARACTERS);
if (!chars) return parsed;
if ((flags & FLAG_WIDTH) && chars > width) chars = width;
if (!(flags & FLAG_DISCARD))
{
char* ptr;
if (flags & FLAG_ALLOC)
{
ptr = (char*)malloc_impl(chars + 1).value_or(nullptr);
if (!ptr) return parsed;
*va_arg(ap, char**) = ptr;
}
else
ptr = va_arg(ap, char*);
memcpy(ptr, str, chars);
ptr[chars] = 0;
}
str += chars;
parsed++;
break;
}
case 'c': {
if (strlen(str) < width) return parsed;
if (!(flags & FLAG_WIDTH)) width = 1;
if (!(flags & FLAG_DISCARD))
{
char* ptr;
if (flags & FLAG_ALLOC)
{
ptr = (char*)malloc_impl(width).value_or(nullptr);
if (!ptr) return parsed;
*va_arg(ap, char**) = ptr;
}
else
ptr = va_arg(ap, char*);
memcpy(ptr, str, width);
}
str += width;
parsed++;
break;
}
case 'd': {
skip_whitespace(&str);
ssize_t value = scan_signed_integer(&str, 10);
if (!(flags & FLAG_DISCARD)) write_parsed_signed_integer(value, flags, ap);
parsed++;
break;
}
case 'i': {
skip_whitespace(&str);
ssize_t value = scan_signed_integer(&str, 0);
if (!(flags & FLAG_DISCARD)) write_parsed_signed_integer(value, flags, ap);
parsed++;
break;
}
case 'o': {
skip_whitespace(&str);
size_t value = scan_unsigned_integer(&str, 8);
if (!(flags & FLAG_DISCARD)) write_parsed_unsigned_integer(value, flags, ap);
parsed++;
break;
}
case 'u': {
skip_whitespace(&str);
size_t value = scan_unsigned_integer(&str, 10);
if (!(flags & FLAG_DISCARD)) write_parsed_unsigned_integer(value, flags, ap);
parsed++;
break;
}
case 'X':
case 'x': {
skip_whitespace(&str);
size_t value = scan_unsigned_integer(&str, 16);
if (!(flags & FLAG_DISCARD)) write_parsed_unsigned_integer(value, flags, ap);
parsed++;
break;
}
case 'p': {
skip_whitespace(&str);
size_t value = scan_unsigned_integer(&str, 16);
if (!(flags & FLAG_DISCARD)) *va_arg(ap, void**) = (void*)value;
parsed++;
break;
}
case 'n': {
if (!(flags & FLAG_DISCARD)) *va_arg(ap, int*) = (int)(str - s);
break;
}
default: {
dbgln("vsscanf: unknown conversion specifier: %%%c\n", specifier);
return parsed;
}
}
}
}
return parsed;
}

View File

@ -68,6 +68,15 @@ namespace os
*/
Result<void> remove(const Path& path);
/**
* @brief Remove a directory tree from the file system recursively, deleting subfiles and subdirectories as
* well.
*
* @param path The path to remove.
* @return Result<void> Whether the operation succeeded.
*/
Result<void> remove_tree(const Path& path);
/**
* @brief Read the target of a symbolic link.
*

View File

@ -12,6 +12,7 @@
#include <luna/CString.h>
#include <os/Directory.h>
#include <os/FileSystem.h>
#include <sys/syscall.h>
#include <unistd.h>
static bool should_skip_entry(const char* name, os::Directory::Filter filter)
@ -32,8 +33,8 @@ namespace os
{
auto dir = TRY(adopt_shared_if_nonnull(new (std::nothrow) Directory({})));
int fd = openat(path.dirfd(), path.name().chars(), O_RDONLY | O_DIRECTORY, 0);
if (fd < 0) return err(errno);
long rc = syscall(SYS_openat, path.dirfd(), path.name().chars(), O_RDONLY | O_DIRECTORY, 0);
int fd = TRY(Result<int>::from_syscall(rc));
DIR* dp = fdopendir(fd);
if (!dp)

View File

@ -10,6 +10,7 @@
#include <errno.h>
#include <luna/StringBuilder.h>
#include <os/File.h>
#include <sys/syscall.h>
#include <unistd.h>
static SharedPtr<os::File> g_stdin = {};
@ -82,8 +83,8 @@ namespace os
{
auto file = TRY(adopt_shared_if_nonnull(new (std::nothrow) File({})));
int fd = openat(path.dirfd(), path.name().chars(), flags, mode);
if (fd < 0) return err(errno);
long rc = syscall(SYS_openat, path.dirfd(), path.name().chars(), flags, mode);
int fd = TRY(Result<int>::from_syscall(rc));
file->m_file = fdopen(fd, stdio_mode_from_openmode(flags));
if (!file->m_file) return err(errno);

View File

@ -10,13 +10,13 @@
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <luna/Buffer.h>
#include <luna/PathParser.h>
#include <luna/String.h>
#include <os/Directory.h>
#include <os/FileSystem.h>
#include <pwd.h>
#include <stdlib.h>
#include <sys/syscall.h>
#include <unistd.h>
namespace os::FileSystem
@ -41,26 +41,39 @@ namespace os::FileSystem
Result<void> stat(const Path& path, struct stat& st, bool follow_symlinks)
{
int rc = fstatat(path.dirfd(), path.name().chars(), &st,
(int)(path.is_empty_path() | (follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW)));
long rc = syscall(SYS_fstatat, path.dirfd(), path.name().chars(), &st,
(int)(path.is_empty_path() | (follow_symlinks ? 0 : AT_SYMLINK_NOFOLLOW)));
if (rc < 0) return err(errno);
return {};
return Result<void>::from_syscall(rc);
}
Result<void> create_directory(StringView path, mode_t mode)
{
int rc = mkdir(path.chars(), mode);
if (rc < 0) return err(errno);
return {};
long rc = syscall(SYS_mkdir, path.chars(), mode);
return Result<void>::from_syscall(rc);
}
Result<void> remove(const Path& path)
{
// FIXME: This will not work on many operating systems that require rmdir() for directories.
int rc = unlinkat(path.dirfd(), path.name().chars(), 0);
if (rc < 0) return err(errno);
return {};
long rc = syscall(SYS_unlinkat, path.dirfd(), path.name().chars(), 0);
return Result<void>::from_syscall(rc);
}
Result<void> remove_tree(const Path& path)
{
auto rc = remove(path);
if (!rc.has_error()) return {};
if (rc.error() != ENOTEMPTY) return rc.release_error();
auto dir = TRY(os::Directory::open(path));
Vector<String> entries = TRY(dir->list_names(os::Directory::Filter::ParentAndBase));
for (const auto& entry : entries) { TRY(remove_tree({ dir->fd(), entry.view() })); }
return remove(path);
}
Result<String> readlink(const Path& path)
@ -69,13 +82,14 @@ namespace os::FileSystem
TRY(stat(path, st, false));
if (!S_ISLNK(st.st_mode)) return String {};
Buffer buf = TRY(Buffer::create_sized(st.st_size + 1));
memset(buf.data(), 0, buf.size());
char* buf = (char*)TRY(calloc_impl(st.st_size + 1, 1));
auto guard = make_scope_guard([buf] { free_impl(buf); });
usize nread = TRY(
Result<usize>::from_syscall(syscall(SYS_readlinkat, path.dirfd(), path.name().chars(), buf, st.st_size)));
ssize_t nread = readlinkat(path.dirfd(), path.name().chars(), (char*)buf.data(), st.st_size);
if (nread < 0) return err(errno);
guard.deactivate();
return String { (char*)buf.release_data(), (usize)nread };
return String { buf, nread };
}
Result<String> working_directory()
@ -98,8 +112,8 @@ namespace os::FileSystem
Result<void> change_directory(StringView path)
{
int rc = chdir(path.chars());
if (rc < 0) return err(errno);
return {};
long rc = syscall(SYS_chdir, path.chars());
return Result<void>::from_syscall(rc);
}
}

View File

@ -9,8 +9,8 @@
#include <errno.h>
#include <os/Process.h>
#include <signal.h>
#include <stdlib.h>
#include <sys/syscall.h>
#include <unistd.h>
extern char** environ;
@ -19,9 +19,8 @@ namespace os
{
Result<pid_t> Process::fork()
{
pid_t pid = ::fork();
if (pid < 0) return err(errno);
return pid;
long rc = syscall(SYS_fork);
return Result<pid_t>::from_syscall(rc);
}
Result<void> Process::exec(StringView path, Slice<String> args, bool search_in_path)
@ -122,16 +121,14 @@ namespace os
Result<pid_t> Process::wait(pid_t child, int* status, int options)
{
pid_t pid = waitpid(child, status, options);
if (pid < 0) return err(errno);
return pid;
long rc = syscall(SYS_waitpid, child, status, options);
return Result<pid_t>::from_syscall(rc);
}
Result<void> Process::kill(pid_t pid, int signo)
{
int rc = ::kill(pid, signo);
if (rc < 0) return err(errno);
return {};
long rc = syscall(SYS_kill, pid, signo);
return Result<void>::from_syscall(rc);
}
[[noreturn]] void Process::exit(int status)

View File

@ -133,8 +133,7 @@ Result<int> luna_main(int argc, char** argv)
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);
os::print("%s@%s:%s%c ", username, hostname, cwd.chars(), prompt_end);
}
auto maybe_cmd = input_file->read_line();