Compare commits
2 Commits
73fbc37841
...
e5259d5849
Author | SHA1 | Date | |
---|---|---|---|
e5259d5849 | |||
2fffeffe41 |
@ -5,7 +5,7 @@ set(CMAKE_CXX_COMPILER_WORKS 1)
|
|||||||
|
|
||||||
set(CMAKE_CROSSCOMPILING true)
|
set(CMAKE_CROSSCOMPILING true)
|
||||||
|
|
||||||
project(Luna LANGUAGES C CXX ASM ASM_NASM VERSION 0.3.0)
|
project(Luna LANGUAGES C CXX ASM ASM_NASM VERSION 0.2.0)
|
||||||
|
|
||||||
set(LUNA_ROOT ${CMAKE_CURRENT_LIST_DIR})
|
set(LUNA_ROOT ${CMAKE_CURRENT_LIST_DIR})
|
||||||
set(LUNA_BASE ${CMAKE_CURRENT_LIST_DIR}/base)
|
set(LUNA_BASE ${CMAKE_CURRENT_LIST_DIR}/base)
|
||||||
|
@ -20,7 +20,8 @@ begin:
|
|||||||
if (rc.error() == EEXIST) return {};
|
if (rc.error() == EEXIST) return {};
|
||||||
if (rc.error() == ENOENT)
|
if (rc.error() == ENOENT)
|
||||||
{
|
{
|
||||||
auto parent = TRY(PathParser::dirname(path));
|
PathParser parser = TRY(PathParser::create(path.chars()));
|
||||||
|
auto parent = TRY(parser.dirname());
|
||||||
|
|
||||||
TRY(mkdir_recursively(parent.view(), (0777 & ~s_umask) | S_IWUSR | S_IXUSR));
|
TRY(mkdir_recursively(parent.view(), (0777 & ~s_umask) | S_IWUSR | S_IXUSR));
|
||||||
|
|
||||||
|
@ -80,14 +80,32 @@ void decode_page_fault_error_code(u64 code)
|
|||||||
|
|
||||||
decode_page_fault_error_code(regs->error);
|
decode_page_fault_error_code(regs->error);
|
||||||
|
|
||||||
CPU::print_stack_trace_at(regs);
|
|
||||||
|
|
||||||
if (!is_in_kernel(regs))
|
if (!is_in_kernel(regs))
|
||||||
{
|
{
|
||||||
// FIXME: Kill this process with SIGSEGV once we have signals and all that.
|
// FIXME: Kill this process with SIGSEGV once we have signals and all that.
|
||||||
kerrorln("Current task %zu was terminated because of a page fault", Scheduler::current()->id);
|
kerrorln("Current task %zu was terminated because of a page fault", Scheduler::current()->id);
|
||||||
Scheduler::current()->exit_and_signal_parent(127);
|
if (Scheduler::current()->is_kernel) Scheduler::current()->state = ThreadState::Dying;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto* current = Scheduler::current();
|
||||||
|
auto* parent = current->parent;
|
||||||
|
if (parent && parent->state == ThreadState::Waiting)
|
||||||
|
{
|
||||||
|
auto child = *parent->child_being_waited_for;
|
||||||
|
if (child == -1 || child == (pid_t)current->id)
|
||||||
|
{
|
||||||
|
parent->child_being_waited_for = (pid_t)current->id;
|
||||||
|
parent->wake_up();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
current->state = ThreadState::Exited;
|
||||||
|
}
|
||||||
|
Scheduler::current()->status = 127;
|
||||||
|
kernel_yield();
|
||||||
|
unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
CPU::print_stack_trace_at(regs);
|
||||||
|
|
||||||
CPU::efficient_halt();
|
CPU::efficient_halt();
|
||||||
}
|
}
|
||||||
@ -100,13 +118,6 @@ void decode_page_fault_error_code(u64 code)
|
|||||||
|
|
||||||
CPU::print_stack_trace_at(regs);
|
CPU::print_stack_trace_at(regs);
|
||||||
|
|
||||||
if (!is_in_kernel(regs))
|
|
||||||
{
|
|
||||||
// FIXME: Kill this process with SIGSEGV once we have signals and all that.
|
|
||||||
kerrorln("Current task %zu was terminated because of a general protection fault", Scheduler::current()->id);
|
|
||||||
Scheduler::current()->exit_and_signal_parent(127);
|
|
||||||
}
|
|
||||||
|
|
||||||
CPU::efficient_halt();
|
CPU::efficient_halt();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -291,14 +302,14 @@ namespace CPU
|
|||||||
asm volatile("hlt");
|
asm volatile("hlt");
|
||||||
}
|
}
|
||||||
|
|
||||||
[[noreturn]] void efficient_halt() // Halt the CPU, using the lowest power possible. On x86-64 we do this using
|
[[noreturn]] void efficient_halt() // Halt the CPU, using the lowest power possible. On x86-64 we do this using the
|
||||||
// the "hlt" instruction, which puts the CPU into a low-power idle state
|
// "hlt" instruction, which puts the CPU into a low-power idle state until the
|
||||||
// until the next interrupt arrives... and we disable interrupts beforehand.
|
// next interrupt arrives... and we disable interrupts beforehand.
|
||||||
{
|
{
|
||||||
asm volatile("cli"); // Disable interrupts
|
asm volatile("cli"); // Disable interrupts
|
||||||
loop:
|
loop:
|
||||||
asm volatile("hlt"); // Let the cpu rest and pause until the next interrupt arrives... which in this case
|
asm volatile("hlt"); // Let the cpu rest and pause until the next interrupt arrives... which in this case should
|
||||||
// should be never (unless an NMI arrives) :)
|
// be never (unless an NMI arrives) :)
|
||||||
goto loop; // Safeguard: if we ever wake up, start our low-power rest again
|
goto loop; // Safeguard: if we ever wake up, start our low-power rest again
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,13 +71,14 @@ namespace VFS
|
|||||||
|
|
||||||
Result<SharedPtr<Inode>> create_directory(const char* path, Credentials auth, SharedPtr<Inode> working_directory)
|
Result<SharedPtr<Inode>> create_directory(const char* path, Credentials auth, SharedPtr<Inode> working_directory)
|
||||||
{
|
{
|
||||||
auto parent_path = TRY(PathParser::dirname(path));
|
auto parser = TRY(PathParser::create(path));
|
||||||
|
auto parent_path = TRY(parser.dirname());
|
||||||
|
|
||||||
auto parent_inode = TRY(resolve_path(parent_path.chars(), auth, working_directory));
|
auto parent_inode = TRY(resolve_path(parent_path.chars(), auth, working_directory));
|
||||||
|
|
||||||
if (!can_write(parent_inode, auth)) return err(EACCES);
|
if (!can_write(parent_inode, auth)) return err(EACCES);
|
||||||
|
|
||||||
auto child_name = TRY(PathParser::basename(path));
|
auto child_name = TRY(parser.basename());
|
||||||
|
|
||||||
TRY(validate_filename(child_name.view()));
|
TRY(validate_filename(child_name.view()));
|
||||||
|
|
||||||
@ -86,13 +87,14 @@ namespace VFS
|
|||||||
|
|
||||||
Result<SharedPtr<Inode>> create_file(const char* path, Credentials auth, SharedPtr<Inode> working_directory)
|
Result<SharedPtr<Inode>> create_file(const char* path, Credentials auth, SharedPtr<Inode> working_directory)
|
||||||
{
|
{
|
||||||
auto parent_path = TRY(PathParser::dirname(path));
|
auto parser = TRY(PathParser::create(path));
|
||||||
|
auto parent_path = TRY(parser.dirname());
|
||||||
|
|
||||||
auto parent_inode = TRY(resolve_path(parent_path.chars(), auth, working_directory));
|
auto parent_inode = TRY(resolve_path(parent_path.chars(), auth, working_directory));
|
||||||
|
|
||||||
if (!can_write(parent_inode, auth)) return err(EACCES);
|
if (!can_write(parent_inode, auth)) return err(EACCES);
|
||||||
|
|
||||||
auto child_name = TRY(PathParser::basename(path));
|
auto child_name = TRY(parser.basename());
|
||||||
|
|
||||||
TRY(validate_filename(child_name.view()));
|
TRY(validate_filename(child_name.view()));
|
||||||
|
|
||||||
@ -187,8 +189,9 @@ namespace VFS
|
|||||||
|
|
||||||
Result<void> pivot_root(const char* new_root, const char* put_old, SharedPtr<VFS::Inode> working_directory)
|
Result<void> pivot_root(const char* new_root, const char* put_old, SharedPtr<VFS::Inode> working_directory)
|
||||||
{
|
{
|
||||||
auto new_root_parent = TRY(PathParser::dirname(new_root));
|
auto root_parser = TRY(PathParser::create(new_root));
|
||||||
auto new_root_path = TRY(PathParser::basename(new_root));
|
auto new_root_parent = TRY(root_parser.dirname());
|
||||||
|
auto new_root_path = TRY(root_parser.basename());
|
||||||
|
|
||||||
auto new_root_parent_inode = TRY(VFS::resolve_path(new_root_parent.chars(), Credentials {}, working_directory));
|
auto new_root_parent_inode = TRY(VFS::resolve_path(new_root_parent.chars(), Credentials {}, working_directory));
|
||||||
auto new_root_inode = TRY(new_root_parent_inode->find(new_root_path.chars()));
|
auto new_root_inode = TRY(new_root_parent_inode->find(new_root_path.chars()));
|
||||||
@ -197,8 +200,9 @@ namespace VFS
|
|||||||
if (!new_root_inode->is_mountpoint()) return err(EINVAL);
|
if (!new_root_inode->is_mountpoint()) return err(EINVAL);
|
||||||
if (new_root_inode->fs() == g_root_inode->fs()) return err(EBUSY);
|
if (new_root_inode->fs() == g_root_inode->fs()) return err(EBUSY);
|
||||||
|
|
||||||
auto parent_path = TRY(PathParser::dirname(put_old));
|
auto parser = TRY(PathParser::create(put_old));
|
||||||
auto child = TRY(PathParser::basename(put_old));
|
auto parent_path = TRY(parser.dirname());
|
||||||
|
auto child = TRY(parser.basename());
|
||||||
|
|
||||||
kdbgln("vfs: Pivoting root from / to %s, using %s as new root", put_old, new_root);
|
kdbgln("vfs: Pivoting root from / to %s, using %s as new root", put_old, new_root);
|
||||||
|
|
||||||
@ -224,8 +228,9 @@ namespace VFS
|
|||||||
Result<void> mount(const char* path, SharedPtr<VFS::FileSystem> fs, Credentials auth,
|
Result<void> mount(const char* path, SharedPtr<VFS::FileSystem> fs, Credentials auth,
|
||||||
SharedPtr<VFS::Inode> working_directory)
|
SharedPtr<VFS::Inode> working_directory)
|
||||||
{
|
{
|
||||||
auto parent_path = TRY(PathParser::dirname(path));
|
auto parser = TRY(PathParser::create(path));
|
||||||
auto child = TRY(PathParser::basename(path));
|
auto parent_path = TRY(parser.dirname());
|
||||||
|
auto child = TRY(parser.basename());
|
||||||
|
|
||||||
kinfoln("vfs: Mounting filesystem on target %s", path);
|
kinfoln("vfs: Mounting filesystem on target %s", path);
|
||||||
|
|
||||||
@ -244,8 +249,9 @@ namespace VFS
|
|||||||
|
|
||||||
Result<void> umount(const char* path, Credentials auth, SharedPtr<VFS::Inode> working_directory)
|
Result<void> umount(const char* path, Credentials auth, SharedPtr<VFS::Inode> working_directory)
|
||||||
{
|
{
|
||||||
auto parent_path = TRY(PathParser::dirname(path));
|
auto parser = TRY(PathParser::create(path));
|
||||||
auto child = TRY(PathParser::basename(path));
|
auto parent_path = TRY(parser.dirname());
|
||||||
|
auto child = TRY(parser.basename());
|
||||||
|
|
||||||
if (child.view() == "/") return err(EBUSY);
|
if (child.view() == "/") return err(EBUSY);
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ Result<u64> UserVM::alloc_region(usize count, bool persistent)
|
|||||||
return err(ENOMEM);
|
return err(ENOMEM);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<bool> UserVM::set_region(u64 address, usize count, bool used, bool persistent)
|
Result<bool> UserVM::set_region(u64 address, usize count, bool used)
|
||||||
{
|
{
|
||||||
if (address >= VM_END) return err(EINVAL);
|
if (address >= VM_END) return err(EINVAL);
|
||||||
|
|
||||||
@ -112,7 +112,6 @@ Result<bool> UserVM::set_region(u64 address, usize count, bool used, bool persis
|
|||||||
if (region->start >= address && region->end <= end)
|
if (region->start >= address && region->end <= end)
|
||||||
{
|
{
|
||||||
region->used = used;
|
region->used = used;
|
||||||
region->persistent = persistent;
|
|
||||||
if (region->start == address && region->end == end)
|
if (region->start == address && region->end == end)
|
||||||
{
|
{
|
||||||
try_merge_region_with_neighbors(region);
|
try_merge_region_with_neighbors(region);
|
||||||
@ -126,7 +125,6 @@ Result<bool> UserVM::set_region(u64 address, usize count, bool used, bool persis
|
|||||||
auto* middle_region = TRY(split_region(region, address));
|
auto* middle_region = TRY(split_region(region, address));
|
||||||
TRY(split_region(middle_region, end));
|
TRY(split_region(middle_region, end));
|
||||||
middle_region->used = used;
|
middle_region->used = used;
|
||||||
middle_region->persistent = persistent;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,7 +133,6 @@ Result<bool> UserVM::set_region(u64 address, usize count, bool used, bool persis
|
|||||||
bool finished = region->end == end;
|
bool finished = region->end == end;
|
||||||
auto* split = TRY(split_region(region, address));
|
auto* split = TRY(split_region(region, address));
|
||||||
split->used = used;
|
split->used = used;
|
||||||
split->persistent = persistent;
|
|
||||||
try_merge_region_with_neighbors(split);
|
try_merge_region_with_neighbors(split);
|
||||||
if (!finished) continue;
|
if (!finished) continue;
|
||||||
return true;
|
return true;
|
||||||
@ -145,7 +142,6 @@ Result<bool> UserVM::set_region(u64 address, usize count, bool used, bool persis
|
|||||||
{
|
{
|
||||||
TRY(split_region(region, end));
|
TRY(split_region(region, end));
|
||||||
region->used = used;
|
region->used = used;
|
||||||
region->persistent = persistent;
|
|
||||||
try_merge_region_with_neighbors(region);
|
try_merge_region_with_neighbors(region);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -21,14 +21,14 @@ class UserVM
|
|||||||
|
|
||||||
Result<u64> alloc_region(usize count, bool persistent = false);
|
Result<u64> alloc_region(usize count, bool persistent = false);
|
||||||
|
|
||||||
Result<bool> test_and_alloc_region(u64 address, usize count, bool persistent = false)
|
Result<bool> test_and_alloc_region(u64 address, usize count)
|
||||||
{
|
{
|
||||||
return set_region(address, count, true, persistent);
|
return set_region(address, count, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<bool> free_region(u64 address, usize count)
|
Result<bool> free_region(u64 address, usize count)
|
||||||
{
|
{
|
||||||
return set_region(address, count, false, false);
|
return set_region(address, count, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Result<OwnedPtr<UserVM>> try_create();
|
static Result<OwnedPtr<UserVM>> try_create();
|
||||||
@ -36,7 +36,7 @@ class UserVM
|
|||||||
Result<OwnedPtr<UserVM>> clone();
|
Result<OwnedPtr<UserVM>> clone();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Result<bool> set_region(u64 address, usize count, bool used, bool persistent);
|
Result<bool> set_region(u64 address, usize count, bool used);
|
||||||
Result<void> create_default_region();
|
Result<void> create_default_region();
|
||||||
Result<void> create_null_region();
|
Result<void> create_null_region();
|
||||||
void try_merge_region_with_neighbors(VMRegion* region);
|
void try_merge_region_with_neighbors(VMRegion* region);
|
||||||
|
@ -7,5 +7,25 @@ Result<u64> sys_exit(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
Thread* current = Scheduler::current();
|
Thread* current = Scheduler::current();
|
||||||
|
|
||||||
current->exit_and_signal_parent(status);
|
Scheduler::for_each_child(current, [](Thread* child) {
|
||||||
|
child->parent = Scheduler::init_thread();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
auto* parent = current->parent;
|
||||||
|
if (parent && parent->state == ThreadState::Waiting)
|
||||||
|
{
|
||||||
|
auto child = *parent->child_being_waited_for;
|
||||||
|
if (child == -1 || child == (pid_t)current->id)
|
||||||
|
{
|
||||||
|
parent->child_being_waited_for = (pid_t)current->id;
|
||||||
|
parent->wake_up();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
current->status = status;
|
||||||
|
current->state = ThreadState::Exited;
|
||||||
|
|
||||||
|
kernel_yield();
|
||||||
|
unreachable();
|
||||||
}
|
}
|
||||||
|
@ -13,8 +13,10 @@ Result<u64> sys_unlinkat(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
Thread* current = Scheduler::current();
|
Thread* current = Scheduler::current();
|
||||||
|
|
||||||
auto dirname = TRY(PathParser::dirname(path.view()));
|
PathParser parser = TRY(PathParser::create(path.chars()));
|
||||||
auto basename = TRY(PathParser::basename(path.view()));
|
|
||||||
|
auto dirname = TRY(parser.dirname());
|
||||||
|
auto basename = TRY(parser.basename());
|
||||||
|
|
||||||
if (basename.view() == ".") return err(EINVAL);
|
if (basename.view() == ".") return err(EINVAL);
|
||||||
|
|
||||||
@ -45,13 +47,14 @@ Result<u64> sys_symlinkat(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Scheduler::current();
|
||||||
|
|
||||||
auto parent = TRY(PathParser::dirname(linkpath.view()));
|
auto parser = TRY(PathParser::create(linkpath.chars()));
|
||||||
|
auto parent = TRY(parser.dirname());
|
||||||
|
|
||||||
auto parent_inode = TRY(current->resolve_atfile(dirfd, parent, false, true));
|
auto parent_inode = TRY(current->resolve_atfile(dirfd, parent, false, true));
|
||||||
|
|
||||||
if (!VFS::can_write(parent_inode, current->auth)) return err(EACCES);
|
if (!VFS::can_write(parent_inode, current->auth)) return err(EACCES);
|
||||||
|
|
||||||
auto child_name = TRY(PathParser::basename(linkpath.view()));
|
auto child_name = TRY(parser.basename());
|
||||||
|
|
||||||
TRY(VFS::validate_filename(child_name.view()));
|
TRY(VFS::validate_filename(child_name.view()));
|
||||||
|
|
||||||
@ -98,7 +101,8 @@ Result<u64> sys_linkat(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
auto* current = Scheduler::current();
|
auto* current = Scheduler::current();
|
||||||
|
|
||||||
auto parent = TRY(PathParser::dirname(newpath.view()));
|
auto parser = TRY(PathParser::create(newpath.chars()));
|
||||||
|
auto parent = TRY(parser.dirname());
|
||||||
|
|
||||||
// FIXME: Use AT_SYMLINK_FOLLOW.
|
// FIXME: Use AT_SYMLINK_FOLLOW.
|
||||||
auto target = TRY(current->resolve_atfile(olddirfd, oldpath, flags & AT_EMPTY_PATH, false));
|
auto target = TRY(current->resolve_atfile(olddirfd, oldpath, flags & AT_EMPTY_PATH, false));
|
||||||
@ -111,7 +115,7 @@ Result<u64> sys_linkat(Registers*, SyscallArgs args)
|
|||||||
|
|
||||||
if (!VFS::can_write(parent_inode, current->auth)) return err(EACCES);
|
if (!VFS::can_write(parent_inode, current->auth)) return err(EACCES);
|
||||||
|
|
||||||
auto child_name = TRY(PathParser::basename(newpath.view()));
|
auto child_name = TRY(parser.basename());
|
||||||
|
|
||||||
TRY(VFS::validate_filename(child_name.view()));
|
TRY(VFS::validate_filename(child_name.view()));
|
||||||
|
|
||||||
|
@ -102,8 +102,9 @@ namespace ELFLoader
|
|||||||
if (can_write_segment(program_header.p_flags)) flags |= MMU::ReadWrite;
|
if (can_write_segment(program_header.p_flags)) flags |= MMU::ReadWrite;
|
||||||
if (can_execute_segment(program_header.p_flags)) flags &= ~MMU::NoExecute;
|
if (can_execute_segment(program_header.p_flags)) flags &= ~MMU::NoExecute;
|
||||||
|
|
||||||
|
// FIXME: Set this memory range to persistent so that munmap() cannot remove it.
|
||||||
if (!TRY(vm->test_and_alloc_region(
|
if (!TRY(vm->test_and_alloc_region(
|
||||||
base_vaddr, get_blocks_from_size(program_header.p_memsz + vaddr_diff, ARCH_PAGE_SIZE), true)))
|
base_vaddr, get_blocks_from_size(program_header.p_memsz + vaddr_diff, ARCH_PAGE_SIZE))))
|
||||||
return err(ENOMEM);
|
return err(ENOMEM);
|
||||||
|
|
||||||
// Allocate physical memory for the segment
|
// Allocate physical memory for the segment
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#include "thread/Thread.h"
|
#include "thread/Thread.h"
|
||||||
#include "memory/MemoryManager.h"
|
#include "memory/MemoryManager.h"
|
||||||
#include "thread/Scheduler.h"
|
|
||||||
#include <bits/atfile.h>
|
#include <bits/atfile.h>
|
||||||
#include <bits/open-flags.h>
|
#include <bits/open-flags.h>
|
||||||
#include <luna/Alloc.h>
|
#include <luna/Alloc.h>
|
||||||
@ -69,33 +68,6 @@ Result<SharedPtr<VFS::Inode>> Thread::resolve_atfile(int dirfd, const String& pa
|
|||||||
return VFS::resolve_path(path.chars(), this->auth, descriptor->inode, follow_last_symlink);
|
return VFS::resolve_path(path.chars(), this->auth, descriptor->inode, follow_last_symlink);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[noreturn]] void Thread::exit_and_signal_parent(u8 _status)
|
|
||||||
{
|
|
||||||
if (is_kernel) state = ThreadState::Dying;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Scheduler::for_each_child(this, [](Thread* child) {
|
|
||||||
child->parent = Scheduler::init_thread();
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (parent && parent->state == ThreadState::Waiting)
|
|
||||||
{
|
|
||||||
auto child = *parent->child_being_waited_for;
|
|
||||||
if (child == -1 || child == (pid_t)id)
|
|
||||||
{
|
|
||||||
parent->child_being_waited_for = (pid_t)id;
|
|
||||||
parent->wake_up();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
state = ThreadState::Exited;
|
|
||||||
}
|
|
||||||
status = _status;
|
|
||||||
kernel_yield();
|
|
||||||
unreachable();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FileDescriptor::should_append()
|
bool FileDescriptor::should_append()
|
||||||
{
|
{
|
||||||
return flags & O_APPEND;
|
return flags & O_APPEND;
|
||||||
|
@ -99,8 +99,6 @@ struct Thread : public LinkedListNode<Thread>
|
|||||||
|
|
||||||
PageDirectory* directory;
|
PageDirectory* directory;
|
||||||
|
|
||||||
[[noreturn]] void exit_and_signal_parent(u8 status);
|
|
||||||
|
|
||||||
bool is_idle()
|
bool is_idle()
|
||||||
{
|
{
|
||||||
return state == ThreadState::Idle;
|
return state == ThreadState::Idle;
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#include "thread/ThreadImage.h"
|
#include "thread/ThreadImage.h"
|
||||||
#include "memory/MemoryManager.h"
|
#include "memory/MemoryManager.h"
|
||||||
#include "thread/Thread.h"
|
#include "thread/Thread.h"
|
||||||
#include <luna/Alignment.h>
|
|
||||||
#include <luna/CString.h>
|
#include <luna/CString.h>
|
||||||
|
|
||||||
static constexpr usize DEFAULT_USER_STACK_PAGES = 6;
|
static constexpr usize DEFAULT_USER_STACK_PAGES = 6;
|
||||||
@ -11,7 +10,8 @@ static Result<void> create_stacks(Stack& user_stack, Stack& kernel_stack, UserVM
|
|||||||
{
|
{
|
||||||
const u64 THREAD_STACK_BASE = 0x10000;
|
const u64 THREAD_STACK_BASE = 0x10000;
|
||||||
|
|
||||||
if (!TRY(vm->test_and_alloc_region(THREAD_STACK_BASE, DEFAULT_USER_STACK_PAGES, true))) return err(ENOMEM);
|
// FIXME: Set this memory range to persistent so that munmap() cannot remove it.
|
||||||
|
if (!TRY(vm->test_and_alloc_region(THREAD_STACK_BASE, DEFAULT_USER_STACK_PAGES))) return err(ENOMEM);
|
||||||
|
|
||||||
TRY(MemoryManager::alloc_at_zeroed(THREAD_STACK_BASE, DEFAULT_USER_STACK_PAGES,
|
TRY(MemoryManager::alloc_at_zeroed(THREAD_STACK_BASE, DEFAULT_USER_STACK_PAGES,
|
||||||
MMU::ReadWrite | MMU::NoExecute | MMU::User));
|
MMU::ReadWrite | MMU::NoExecute | MMU::User));
|
||||||
@ -122,7 +122,7 @@ void ThreadImage::apply(Thread* thread)
|
|||||||
|
|
||||||
thread->kernel_stack = m_kernel_stack;
|
thread->kernel_stack = m_kernel_stack;
|
||||||
thread->stack = m_user_stack;
|
thread->stack = m_user_stack;
|
||||||
thread->set_sp(align_down<16>(m_sp));
|
thread->set_sp(m_sp);
|
||||||
|
|
||||||
thread->directory = m_directory;
|
thread->directory = m_directory;
|
||||||
|
|
||||||
|
@ -20,7 +20,6 @@ set(SOURCES
|
|||||||
src/pwd.cpp
|
src/pwd.cpp
|
||||||
src/grp.cpp
|
src/grp.cpp
|
||||||
src/locale.cpp
|
src/locale.cpp
|
||||||
src/scanf.cpp
|
|
||||||
src/sys/stat.cpp
|
src/sys/stat.cpp
|
||||||
src/sys/mman.cpp
|
src/sys/mman.cpp
|
||||||
src/sys/wait.cpp
|
src/sys/wait.cpp
|
||||||
|
@ -27,9 +27,6 @@ extern FILE* stderr;
|
|||||||
#define stderr stderr
|
#define stderr stderr
|
||||||
|
|
||||||
#define BUFSIZ 1024
|
#define BUFSIZ 1024
|
||||||
#define FILENAME_MAX \
|
|
||||||
1024 // As Luna does not impose a limit on this, this is the recommended size for character arrays holding a file
|
|
||||||
// name.
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C"
|
extern "C"
|
||||||
@ -44,9 +41,6 @@ extern "C"
|
|||||||
/* Bind a stream to a file descriptor. */
|
/* Bind a stream to a file descriptor. */
|
||||||
FILE* fdopen(int fd, const char* mode);
|
FILE* fdopen(int fd, const char* mode);
|
||||||
|
|
||||||
/* Change the underlying file and mode of a stream. */
|
|
||||||
FILE* freopen(const char* path, const char* mode, FILE* stream);
|
|
||||||
|
|
||||||
/* Close a file and frees up its stream. */
|
/* Close a file and frees up its stream. */
|
||||||
int fclose(FILE* stream);
|
int fclose(FILE* stream);
|
||||||
|
|
||||||
@ -101,9 +95,6 @@ extern "C"
|
|||||||
/* Read a character from standard input. */
|
/* Read a character from standard input. */
|
||||||
int getchar(void);
|
int getchar(void);
|
||||||
|
|
||||||
/* Push a character back to stream so that it can be read again. */
|
|
||||||
int ungetc(int c, FILE* stream);
|
|
||||||
|
|
||||||
/* Read a line from stream. */
|
/* Read a line from stream. */
|
||||||
char* fgets(char* buf, size_t size, FILE* stream);
|
char* fgets(char* buf, size_t size, FILE* stream);
|
||||||
|
|
||||||
@ -131,34 +122,16 @@ extern "C"
|
|||||||
int snprintf(char* buf, size_t max, const char* format, ...);
|
int snprintf(char* buf, size_t max, const char* format, ...);
|
||||||
|
|
||||||
/* Write formatted output into a buffer. */
|
/* Write formatted output into a buffer. */
|
||||||
int vsprintf(char* buf, const char* format, va_list ap);
|
int vsprintf(char*, const char*, va_list);
|
||||||
|
|
||||||
/* Write up to max bytes of formatted output into a buffer. */
|
/* Write up to max bytes of formatted output into a buffer. */
|
||||||
int vsnprintf(char* buf, size_t max, const char* format, va_list ap);
|
int vsnprintf(char*, size_t, const char*, va_list);
|
||||||
|
|
||||||
/* Write formatted output to standard output. */
|
/* Write formatted output to standard output. */
|
||||||
int vprintf(const char* format, va_list ap);
|
int vprintf(const char*, va_list ap);
|
||||||
|
|
||||||
/* Write formatted output to standard output. */
|
/* Write formatted output to standard output. */
|
||||||
int printf(const char* format, ...);
|
int printf(const char*, ...);
|
||||||
|
|
||||||
/* Scan formatted input from a string. */
|
|
||||||
int vsscanf(const char* str, const char* format, va_list ap);
|
|
||||||
|
|
||||||
/* Scan formatted input from a string. */
|
|
||||||
int sscanf(const char* str, const char* format, ...);
|
|
||||||
|
|
||||||
/* Scan formatted input from a file. */
|
|
||||||
int vfscanf(FILE* stream, const char* format, va_list ap);
|
|
||||||
|
|
||||||
/* Scan formatted input from a file. */
|
|
||||||
int fscanf(FILE* stream, const char* format, ...);
|
|
||||||
|
|
||||||
/* Scan formatted input from standard input. */
|
|
||||||
int vscanf(const char* format, va_list ap);
|
|
||||||
|
|
||||||
/* Scan formatted input from standard input. */
|
|
||||||
int scanf(const char* format, ...);
|
|
||||||
|
|
||||||
/* Write a string followed by a newline to standard output. */
|
/* Write a string followed by a newline to standard output. */
|
||||||
int puts(const char* s);
|
int puts(const char* s);
|
||||||
|
@ -1,263 +0,0 @@
|
|||||||
#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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -11,13 +11,6 @@ FILE* stdin = nullptr;
|
|||||||
FILE* stderr = nullptr;
|
FILE* stderr = nullptr;
|
||||||
FILE* stdout = nullptr;
|
FILE* stdout = nullptr;
|
||||||
|
|
||||||
static const char* read_tmpdir()
|
|
||||||
{
|
|
||||||
const char* tmpdir = getenv("TMPDIR");
|
|
||||||
if (!tmpdir) return "/tmp";
|
|
||||||
return tmpdir;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int fopen_parse_mode(const char* mode)
|
static int fopen_parse_mode(const char* mode)
|
||||||
{
|
{
|
||||||
int result = 0;
|
int result = 0;
|
||||||
@ -53,12 +46,6 @@ static int fdopen_check_compatible_mode(int fd, int new_flags)
|
|||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
{
|
{
|
||||||
int fflush(FILE*)
|
|
||||||
{
|
|
||||||
// FIXME: Files are not buffered right now.
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
FILE* fopen(const char* path, const char* mode)
|
FILE* fopen(const char* path, const char* mode)
|
||||||
{
|
{
|
||||||
int flags;
|
int flags;
|
||||||
@ -98,25 +85,6 @@ extern "C"
|
|||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE* freopen(const char* path, const char* mode, FILE* stream)
|
|
||||||
{
|
|
||||||
int flags;
|
|
||||||
|
|
||||||
if ((flags = fopen_parse_mode(mode)) < 0) return nullptr;
|
|
||||||
|
|
||||||
close(stream->_fd);
|
|
||||||
|
|
||||||
if (!path) { fail("FIXME: freopen() called with path=nullptr"); }
|
|
||||||
|
|
||||||
int fd = open(path, flags, 0666);
|
|
||||||
if (fd < 0) { return nullptr; }
|
|
||||||
|
|
||||||
stream->_fd = fd;
|
|
||||||
clearerr(stream);
|
|
||||||
|
|
||||||
return stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
int fclose(FILE* stream)
|
int fclose(FILE* stream)
|
||||||
{
|
{
|
||||||
if (close(stream->_fd) < 0) return EOF;
|
if (close(stream->_fd) < 0) return EOF;
|
||||||
@ -413,54 +381,6 @@ extern "C"
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
int sscanf(const char* str, const char* format, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
va_start(ap, format);
|
|
||||||
|
|
||||||
int rc = vsscanf(str, format, ap);
|
|
||||||
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
int vfscanf(FILE* stream, const char* format, va_list ap)
|
|
||||||
{
|
|
||||||
char buf[BUFSIZ];
|
|
||||||
if (!fgets(buf, sizeof(buf), stream)) return EOF;
|
|
||||||
return vsscanf(buf, format, ap);
|
|
||||||
}
|
|
||||||
|
|
||||||
int fscanf(FILE* stream, const char* format, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
va_start(ap, format);
|
|
||||||
|
|
||||||
int rc = vfscanf(stream, format, ap);
|
|
||||||
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
int vscanf(const char* format, va_list ap)
|
|
||||||
{
|
|
||||||
return vfscanf(stdin, format, ap);
|
|
||||||
}
|
|
||||||
|
|
||||||
int scanf(const char* format, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
va_start(ap, format);
|
|
||||||
|
|
||||||
int rc = vfscanf(stdin, format, ap);
|
|
||||||
|
|
||||||
va_end(ap);
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
int puts(const char* s)
|
int puts(const char* s)
|
||||||
{
|
{
|
||||||
if (fputs(s, stdout) < 0) return -1;
|
if (fputs(s, stdout) < 0) return -1;
|
||||||
@ -484,16 +404,12 @@ extern "C"
|
|||||||
|
|
||||||
FILE* tmpfile()
|
FILE* tmpfile()
|
||||||
{
|
{
|
||||||
int fd = open(read_tmpdir(), O_RDWR | O_TMPFILE, 0600);
|
// FIXME: use /tmp as the directory when the tmpfs is mounted only there.
|
||||||
|
int fd = open("/", O_RDWR | O_TMPFILE, 0600);
|
||||||
if (fd < 0) return nullptr;
|
if (fd < 0) return nullptr;
|
||||||
|
|
||||||
FILE* f = fdopen(fd, "w+b");
|
FILE* f = fdopen(fd, "w+b");
|
||||||
if (!f) close(fd);
|
if (!f) close(fd);
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ungetc(int, FILE*)
|
|
||||||
{
|
|
||||||
fail("FIXME: ungetc: not implemented");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,10 @@
|
|||||||
#include <luna/Types.h>
|
#include <luna/Types.h>
|
||||||
|
|
||||||
// Parse an unsigned integer and advance *str to point to the first non-digit character after the number.
|
// Parse an unsigned integer and advance *str to point to the first non-digit character after the number.
|
||||||
usize scan_unsigned_integer(const char** str, int base = 10);
|
usize scan_unsigned_integer(const char** str);
|
||||||
|
|
||||||
// Parse a signed integer and advance *str to point to the first non-digit character after the number.
|
// Parse a signed integer and advance *str to point to the first non-digit character after the number.
|
||||||
isize scan_signed_integer(const char** str, int base = 10);
|
isize scan_signed_integer(const char** str);
|
||||||
|
|
||||||
// Parse an unsigned integer, similar to strtoull().
|
// Parse an unsigned integer, similar to strtoull().
|
||||||
usize parse_unsigned_integer(const char* str, const char** endptr, int base);
|
usize parse_unsigned_integer(const char* str, const char** endptr, int base);
|
||||||
|
@ -37,8 +37,8 @@ class PathParser
|
|||||||
return m_already_called_next ? (bool)m_strtok_saved_state : is_not_delim(*m_copy);
|
return m_already_called_next ? (bool)m_strtok_saved_state : is_not_delim(*m_copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Result<String> basename(StringView path);
|
Result<String> basename();
|
||||||
static Result<String> dirname(StringView path);
|
Result<String> dirname();
|
||||||
|
|
||||||
Option<const char*> next();
|
Option<const char*> next();
|
||||||
|
|
||||||
|
@ -212,7 +212,7 @@ template <typename T> class Vector
|
|||||||
{
|
{
|
||||||
Vector<T> other;
|
Vector<T> other;
|
||||||
TRY(other.try_reserve(m_capacity));
|
TRY(other.try_reserve(m_capacity));
|
||||||
memcpy(other.m_data, m_data, m_size * sizeof(T));
|
memcpy(other.m_data, m_data, m_size);
|
||||||
other.m_size = m_size;
|
other.m_size = m_size;
|
||||||
return other;
|
return other;
|
||||||
}
|
}
|
||||||
|
@ -150,7 +150,7 @@ static usize parse_precision(const char** format, flags_t& flags, va_list ap)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void parse_type(const char** format, flags_t& flags)
|
static void parse_length(const char** format, flags_t& flags)
|
||||||
{
|
{
|
||||||
// FIXME: Support %j (intmax_t/uintmax_t)
|
// FIXME: Support %j (intmax_t/uintmax_t)
|
||||||
switch (**format)
|
switch (**format)
|
||||||
@ -415,7 +415,7 @@ Result<usize> cstyle_format(const char* format, callback_t callback, void* arg,
|
|||||||
flags_t flags = parse_flags(&format);
|
flags_t flags = parse_flags(&format);
|
||||||
const usize width = parse_width(&format, flags, ap);
|
const usize width = parse_width(&format, flags, ap);
|
||||||
usize precision = parse_precision(&format, flags, ap);
|
usize precision = parse_precision(&format, flags, ap);
|
||||||
parse_type(&format, flags);
|
parse_length(&format, flags);
|
||||||
|
|
||||||
conv_state vstate = { flags, width, precision };
|
conv_state vstate = { flags, width, precision };
|
||||||
|
|
||||||
@ -503,18 +503,15 @@ usize vstring_format(char* buf, usize max, const char* format, va_list ap)
|
|||||||
[](char c, void* arg) -> Result<void> {
|
[](char c, void* arg) -> Result<void> {
|
||||||
StringFormatInfo* info_arg = (StringFormatInfo*)arg;
|
StringFormatInfo* info_arg = (StringFormatInfo*)arg;
|
||||||
if (!info_arg->remaining) return {};
|
if (!info_arg->remaining) return {};
|
||||||
if (info_arg->buffer)
|
|
||||||
{
|
|
||||||
*(info_arg->buffer) = c;
|
*(info_arg->buffer) = c;
|
||||||
info_arg->buffer++;
|
info_arg->buffer++;
|
||||||
}
|
|
||||||
info_arg->remaining--;
|
info_arg->remaining--;
|
||||||
return {};
|
return {};
|
||||||
},
|
},
|
||||||
&info, ap)
|
&info, ap)
|
||||||
.value();
|
.value();
|
||||||
|
|
||||||
if (info.buffer) *(info.buffer) = 0;
|
*(info.buffer) = 0;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -89,12 +89,12 @@ isize parse_signed_integer(const char* str, const char** endptr, int base)
|
|||||||
return negative ? -(isize)rc : (isize)rc;
|
return negative ? -(isize)rc : (isize)rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
usize scan_unsigned_integer(const char** str, int base)
|
usize scan_unsigned_integer(const char** str)
|
||||||
{
|
{
|
||||||
return parse_unsigned_integer(*str, str, base);
|
return parse_unsigned_integer(*str, str, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
isize scan_signed_integer(const char** str, int base)
|
isize scan_signed_integer(const char** str)
|
||||||
{
|
{
|
||||||
return parse_signed_integer(*str, str, base);
|
return parse_signed_integer(*str, str, 10);
|
||||||
}
|
}
|
||||||
|
@ -35,9 +35,9 @@ Option<const char*> PathParser::next()
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<String> PathParser::basename(StringView path)
|
Result<String> PathParser::basename()
|
||||||
{
|
{
|
||||||
char* copy = strdup(path.chars());
|
char* copy = strdup(m_original);
|
||||||
if (!copy) return err(ENOMEM);
|
if (!copy) return err(ENOMEM);
|
||||||
|
|
||||||
auto guard = make_scope_guard([copy] { free_impl(copy); });
|
auto guard = make_scope_guard([copy] { free_impl(copy); });
|
||||||
@ -48,9 +48,9 @@ Result<String> PathParser::basename(StringView path)
|
|||||||
return String::from_cstring(result);
|
return String::from_cstring(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
Result<String> PathParser::dirname(StringView path)
|
Result<String> PathParser::dirname()
|
||||||
{
|
{
|
||||||
char* copy = strdup(path.chars());
|
char* copy = strdup(m_original);
|
||||||
if (!copy) return err(ENOMEM);
|
if (!copy) return err(ENOMEM);
|
||||||
|
|
||||||
auto guard = make_scope_guard([copy] { free_impl(copy); });
|
auto guard = make_scope_guard([copy] { free_impl(copy); });
|
||||||
|
@ -6,5 +6,5 @@ Stack::Stack(u64 base, usize bytes) : m_base(base), m_bytes(bytes)
|
|||||||
|
|
||||||
u64 Stack::top() const
|
u64 Stack::top() const
|
||||||
{
|
{
|
||||||
return (m_base + m_bytes) - 16;
|
return (m_base + m_bytes) - sizeof(void*);
|
||||||
}
|
}
|
||||||
|
@ -118,7 +118,7 @@ namespace os
|
|||||||
|
|
||||||
bool is_still_parsing_flags = true;
|
bool is_still_parsing_flags = true;
|
||||||
|
|
||||||
Vector<PositionalArgument> positional_args = TRY(m_positional_args.shallow_copy());
|
Vector<PositionalArgument> positional_args = TRY(m_positional_args.deep_copy());
|
||||||
|
|
||||||
for (int i = 1; i < argc; i++)
|
for (int i = 1; i < argc; i++)
|
||||||
{
|
{
|
||||||
|
@ -19,7 +19,6 @@ luna_test(libluna/TestUtf8.cpp TestUtf8)
|
|||||||
luna_test(libluna/TestFormat.cpp TestFormat)
|
luna_test(libluna/TestFormat.cpp TestFormat)
|
||||||
luna_test(libluna/TestHashTable.cpp TestHashTable)
|
luna_test(libluna/TestHashTable.cpp TestHashTable)
|
||||||
luna_test(libluna/TestCPath.cpp TestCPath)
|
luna_test(libluna/TestCPath.cpp TestCPath)
|
||||||
luna_test(libc/TestScanf.cpp TestScanf)
|
|
||||||
|
|
||||||
luna_app(run-tests.cpp run-tests)
|
luna_app(run-tests.cpp run-tests)
|
||||||
endif()
|
endif()
|
||||||
|
@ -1,91 +0,0 @@
|
|||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <test.h>
|
|
||||||
|
|
||||||
// FIXME: Add more tests.
|
|
||||||
|
|
||||||
TestResult test_basic_scanf()
|
|
||||||
{
|
|
||||||
char hello[21];
|
|
||||||
char world[21];
|
|
||||||
|
|
||||||
int parsed = sscanf("hello world", "%20s %20s", hello, world);
|
|
||||||
validate(parsed == 2);
|
|
||||||
|
|
||||||
validate(!strcmp(hello, "hello"));
|
|
||||||
validate(!strcmp(world, "world"));
|
|
||||||
|
|
||||||
test_success;
|
|
||||||
}
|
|
||||||
|
|
||||||
TestResult test_incomplete_scanf()
|
|
||||||
{
|
|
||||||
char hello[21];
|
|
||||||
char world[21];
|
|
||||||
|
|
||||||
int parsed = sscanf("hello ", "%20s %20s", hello, world);
|
|
||||||
validate(parsed == 1);
|
|
||||||
|
|
||||||
validate(!strcmp(hello, "hello"));
|
|
||||||
|
|
||||||
test_success;
|
|
||||||
}
|
|
||||||
|
|
||||||
TestResult test_integer_scanf()
|
|
||||||
{
|
|
||||||
int hour;
|
|
||||||
int min;
|
|
||||||
|
|
||||||
int parsed = sscanf("23:59", "%d:%d", &hour, &min);
|
|
||||||
validate(parsed == 2);
|
|
||||||
|
|
||||||
validate(hour == 23);
|
|
||||||
validate(min == 59);
|
|
||||||
|
|
||||||
test_success;
|
|
||||||
}
|
|
||||||
|
|
||||||
TestResult test_integer_auto_base_scanf()
|
|
||||||
{
|
|
||||||
int a;
|
|
||||||
int b;
|
|
||||||
int c;
|
|
||||||
|
|
||||||
int parsed = sscanf("65, \t0x23, 0755", "%i, %i, %i", &a, &b, &c);
|
|
||||||
validate(parsed == 3);
|
|
||||||
|
|
||||||
validate(a == 65);
|
|
||||||
validate(b == 0x23);
|
|
||||||
validate(c == 0755);
|
|
||||||
|
|
||||||
test_success;
|
|
||||||
}
|
|
||||||
|
|
||||||
TestResult test_scanf_characters_consumed()
|
|
||||||
{
|
|
||||||
int hour;
|
|
||||||
int min;
|
|
||||||
int nr_chars;
|
|
||||||
|
|
||||||
int parsed = sscanf("23:59", "%d:%d%n", &hour, &min, &nr_chars);
|
|
||||||
validate(parsed == 2);
|
|
||||||
|
|
||||||
validate(hour == 23);
|
|
||||||
validate(min == 59);
|
|
||||||
validate(nr_chars == 5);
|
|
||||||
|
|
||||||
test_success;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result<void> test_main()
|
|
||||||
{
|
|
||||||
test_prelude;
|
|
||||||
|
|
||||||
run_test(test_basic_scanf);
|
|
||||||
run_test(test_incomplete_scanf);
|
|
||||||
run_test(test_integer_scanf);
|
|
||||||
run_test(test_integer_auto_base_scanf);
|
|
||||||
run_test(test_scanf_characters_consumed);
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user