diff --git a/lib/std/core/allocators/heap_allocator.c3 b/lib/std/core/allocators/heap_allocator.c3 index 5fe6db54d..5b7aae96b 100644 --- a/lib/std/core/allocators/heap_allocator.c3 +++ b/lib/std/core/allocators/heap_allocator.c3 @@ -21,71 +21,35 @@ struct SimpleHeapAllocator fn void SimpleHeapAllocator.init(SimpleHeapAllocator* this, MemoryAllocFn allocator) { this.alloc_fn = allocator; - this.allocator = { &simple_heap_allocator_function }; + static AllocatorFunction alloc_fn = allocator_fn(SimpleHeapAllocator); + this.allocator = { alloc_fn }; this.free_list = null; } -/** - * @require !alignment || math::is_power_of_2(alignment) - * @require this `unexpectedly missing the allocator` - */ -fn void*! simple_heap_allocator_function(Allocator* this, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind) @private -{ - SimpleHeapAllocator* heap = (SimpleHeapAllocator*)this; - switch (kind) - { - case ALIGNED_ALLOC: - return @aligned_alloc(heap._alloc, size, alignment, offset); - case ALLOC: - return heap._alloc(size); - case ALIGNED_CALLOC: - return @aligned_calloc(heap._calloc, size, alignment, offset); - case CALLOC: - return heap._calloc(size); - case ALIGNED_REALLOC: - if (!size) nextcase ALIGNED_FREE; - if (!old_pointer) nextcase ALIGNED_CALLOC; - return @aligned_realloc(heap._calloc, heap._free, old_pointer, size, alignment, offset); - case REALLOC: - if (!size) nextcase FREE; - if (!old_pointer) nextcase CALLOC; - return heap._realloc(old_pointer, size); - case RESET: - return AllocationFailure.UNSUPPORTED_OPERATION?; - case ALIGNED_FREE: - @aligned_free(heap._free, old_pointer)!; - return null; - case FREE: - heap._free(old_pointer); - return null; - default: - unreachable(); - } -} /** * @require this && old_pointer && bytes > 0 **/ -fn void*! SimpleHeapAllocator._realloc(SimpleHeapAllocator* this, void* old_pointer, usz bytes) +fn void*! SimpleHeapAllocator.realloc(SimpleHeapAllocator* this, void* old_pointer, usz bytes) { // Find the block header. Header* block = (Header*)old_pointer - 1; if (block.size >= bytes) return old_pointer; - void* new = this._alloc(bytes)!; + void* new = this.alloc(bytes)!; usz max_to_copy = math::min(block.size, bytes); mem::copy(new, old_pointer, max_to_copy); - this._free(old_pointer); + this.free(old_pointer); return new; } -fn void*! SimpleHeapAllocator._calloc(SimpleHeapAllocator* this, usz bytes) @local +fn void*! SimpleHeapAllocator.calloc(SimpleHeapAllocator* this, usz bytes) @local { - void* data = this._alloc(bytes)!; + void* data = this.alloc(bytes)!; mem::clear(data, bytes, mem::DEFAULT_MEM_ALIGNMENT); return data; } -fn void*! SimpleHeapAllocator._alloc(SimpleHeapAllocator* this, usz bytes) @local +fn void*! SimpleHeapAllocator.alloc(SimpleHeapAllocator* this, usz bytes) @local { usz aligned_bytes = mem::aligned_offset(bytes, mem::DEFAULT_MEM_ALIGNMENT); if (!this.free_list) @@ -141,11 +105,11 @@ fn void! SimpleHeapAllocator.add_block(SimpleHeapAllocator* this, usz aligned_by Header* new_block = (Header*)result.ptr; new_block.size = result.len - Header.sizeof; new_block.next = null; - this._free(new_block + 1); + this.free(new_block + 1); } -fn void SimpleHeapAllocator._free(SimpleHeapAllocator* this, void* ptr) @local +fn void SimpleHeapAllocator.free(SimpleHeapAllocator* this, void* ptr) @local { // Empty ptr -> do nothing. if (!ptr) return; diff --git a/lib/std/core/env.c3 b/lib/std/core/env.c3 index 384d5399e..5d1f943cf 100644 --- a/lib/std/core/env.c3 +++ b/lib/std/core/env.c3 @@ -116,7 +116,8 @@ enum ArchType const OsType OS_TYPE = (OsType)$$OS_TYPE; const ArchType ARCH_TYPE = (ArchType)$$ARCH_TYPE; -const bool COMPILER_LIBC_AVAILABLE = $$COMPILER_LIBC_AVAILABLE; +const bool LIBC = $$COMPILER_LIBC_AVAILABLE; +const bool NO_LIBC = !$$COMPILER_LIBC_AVAILABLE; const CompilerOptLevel COMPILER_OPT_LEVEL = (CompilerOptLevel)$$COMPILER_OPT_LEVEL; const bool BIG_ENDIAN = $$PLATFORM_BIG_ENDIAN; const bool I128_NATIVE_SUPPORT = $$PLATFORM_I128_SUPPORTED; @@ -128,27 +129,18 @@ const bool BENCHMARKING = $$BENCHMARKING; const bool TESTING = $$TESTING; const MemoryEnvironment MEMORY_ENV = (MemoryEnvironment)$$MEMORY_ENVIRONMENT; -const LINUX_LIBC @builtin = env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == LINUX; -const DARWIN_LIBC @builtin = env::COMPILER_LIBC_AVAILABLE && env::os_is_darwin(); -const WIN32_LIBC @builtin = env::COMPILER_LIBC_AVAILABLE && env::os_is_win32(); -const POSIX_LIBC @builtin = env::COMPILER_LIBC_AVAILABLE && env::os_is_posix(); -const OPENBSD_LIBC @builtin = env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == OPENBSD; -const FREEBSD_LIBC @builtin = env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == FREEBSD; -const NETBSD_LIBC @builtin = env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == NETBSD; -const WASI_LIBC @builtin = env::COMPILER_LIBC_AVAILABLE && env::OS_TYPE == WASI; -const WASM_NOLIBC @builtin = !env::COMPILER_LIBC_AVAILABLE && env::ARCH_TYPE == ArchType.WASM32 || env::ARCH_TYPE == ArchType.WASM64; -const bool NO_LIBC = !env::COMPILER_LIBC_AVAILABLE; -const bool LIBC = env::COMPILER_LIBC_AVAILABLE; -const bool WIN32 = OS_TYPE == WIN32; -const bool DARWIN = OS_TYPE == IOS || OS_TYPE == MACOS || OS_TYPE == TVOS || OS_TYPE == WATCHOS; -const bool LINUX = OS_TYPE == LINUX; -const bool POSIX = os_is_posix(); -const bool WASI = OS_TYPE == WASI; +const bool X86_64 = ARCH_TYPE == X86_64; +const bool AARCH64 = ARCH_TYPE == AARCH64; -macro bool os_is_win32() -{ - return OS_TYPE == WIN32; -} +const bool LINUX = LIBC && OS_TYPE == LINUX; +const bool DARWIN = LIBC && os_is_darwin(); +const bool WIN32 = LIBC && OS_TYPE == WIN32; +const bool POSIX = LIBC && os_is_posix(); +const bool OPENBSD = LIBC && OS_TYPE == OPENBSD; +const bool FREEBSD = LIBC && OS_TYPE == FREEBSD; +const bool NETBSD = LIBC && OS_TYPE == NETBSD; +const bool WASI = LIBC && OS_TYPE == WASI; +const bool WASM_NOLIBC @builtin = !LIBC && ARCH_TYPE == ArchType.WASM32 || ARCH_TYPE == ArchType.WASM64; macro bool os_is_darwin() { @@ -194,7 +186,7 @@ macro bool os_is_posix() **/ fn String! get_var(String name) { - $if COMPILER_LIBC_AVAILABLE && !WIN32_LIBC: + $if LIBC && !WIN32: @pool() { ZString val = libc::getenv(name.zstr_tcopy()); @@ -213,7 +205,7 @@ fn String! get_var(String name) **/ fn void set_var(String name, String value, bool overwrite = true) { - $if COMPILER_LIBC_AVAILABLE && !WIN32_LIBC: + $if LIBC && !WIN32: @pool() { if (libc::setenv(name.zstr_tcopy(), value.zstr_copy(), (int)overwrite)) @@ -230,7 +222,7 @@ fn void set_var(String name, String value, bool overwrite = true) **/ fn void clear_var(String name) { - $if COMPILER_LIBC_AVAILABLE && !WIN32_LIBC: + $if LIBC && !WIN32: @pool() { if (libc::unsetenv(name.zstr_tcopy())) diff --git a/lib/std/core/mem_allocator.c3 b/lib/std/core/mem_allocator.c3 index fd12e0d8f..01312364e 100644 --- a/lib/std/core/mem_allocator.c3 +++ b/lib/std/core/mem_allocator.c3 @@ -8,6 +8,69 @@ const Allocator* LIBC_ALLOCATOR = &_SYSTEM_ALLOCATOR; def AllocatorFunction = fn void*!(Allocator* allocator, usz new_size, usz alignment, usz offset, void* old_pointer, AllocationKind kind); + +macro AllocatorFunction allocator_fn($AllocatorType) +{ + return fn void*!(Allocator* data, usz size, usz alignment, usz offset, void* old_pointer, AllocationKind kind) + { + $AllocatorType* allocator = ($AllocatorType*)data; + bool $supports_aligned = $defined(allocator.aligned_alloc); + switch (kind) + { + case CALLOC: + return allocator.calloc(size) @inline; + case ALIGNED_CALLOC: + $if $supports_aligned: + return allocator.aligned_calloc(size, alignment, offset) @inline; + $else + return @aligned_calloc(allocator.calloc, size, alignment, offset); + $endif + case ALLOC: + return allocator.alloc(size); + case ALIGNED_ALLOC: + $if $supports_aligned: + return allocator.aligned_alloc(size, alignment, offset) @inline; + $else + return @aligned_alloc(allocator.alloc, size, alignment, offset); + $endif + case REALLOC: + return allocator.realloc(old_pointer, size) @inline; + case ALIGNED_REALLOC: + $if $supports_aligned: + return allocator.aligned_realloc(old_pointer, size, alignment, offset) @inline; + $else + return @aligned_realloc(allocator.alloc, allocator.free, old_pointer, size, alignment, offset); + $endif + case ALIGNED_FREE: + $if $supports_aligned: + allocator.aligned_free(old_pointer, alignment, offset) @inline!; + return null; + $else + @aligned_free(allocator.free, old_pointer) @inline!; + return null; + $endif + case FREE: + allocator.free(old_pointer)!; + return null; + case MARK: + $if $defined(allocator.mark): + allocator.mark() @inline; + return null; + $else + return AllocationFailure.UNSUPPORTED_OPERATION?; + $endif + case RESET: + $if $defined(allocator.reset): + allocator.reset() @inline; + return null; + $else + return AllocationFailure.UNSUPPORTED_OPERATION?; + $endif + } + unreachable(); + }; +} + struct Allocator { AllocatorFunction function; diff --git a/lib/std/core/private/main_stub.c3 b/lib/std/core/private/main_stub.c3 index 524c619e3..703afd257 100644 --- a/lib/std/core/private/main_stub.c3 +++ b/lib/std/core/private/main_stub.c3 @@ -55,7 +55,7 @@ macro int @main_to_void_main_args(#m, int argc, char** argv) return 0; } -module std::core::main_stub @if(env::os_is_win32()); +module std::core::main_stub @if(env::WIN32); extern fn Char16** _win_command_line_to_argv_w(ushort* cmd_line, int* argc_ptr) @extern("CommandLineToArgvW"); diff --git a/lib/std/io/dir.c3 b/lib/std/io/dir.c3 deleted file mode 100644 index 5aa31b84c..000000000 --- a/lib/std/io/dir.c3 +++ /dev/null @@ -1,3 +0,0 @@ -module std::io::dir; -import std::io::os; - diff --git a/lib/std/io/io.c3 b/lib/std/io/io.c3 index 144edf7b2..ee3be5334 100644 --- a/lib/std/io/io.c3 +++ b/lib/std/io/io.c3 @@ -45,12 +45,9 @@ fault IoError GENERAL_ERROR, UNKNOWN_ERROR, UNSUPPORTED_OPERATION, + FILE_CANNOT_DELETE, } -fn void putchar(char c) @inline -{ - libc::putchar(c); -} macro void print(x) { @@ -90,6 +87,14 @@ macro void printn(x = "") $endswitch } +module std::io @if (env::LIBC); +import libc; + +fn void putchar(char c) @inline +{ + libc::putchar(c); +} + fn File stdout() { return { libc::stdout() }; @@ -105,6 +110,33 @@ fn File stdin() return { libc::stdin() }; } +module std::io @if(!env::LIBC); + +File stdin_file; +File stdout_file; +File stderr_file; + +fn void putchar(char c) @inline +{ + (void)stdout_file.putc(c); +} + +fn File stdout() +{ + return stdout_file; +} + +fn File stderr() +{ + return stderr_file; +} + +fn File stdin() +{ + return stdin_file; +} + + /* diff --git a/lib/std/io/io_file.c3 b/lib/std/io/io_file.c3 index e3d8227b3..21f9b03e3 100644 --- a/lib/std/io/io_file.c3 +++ b/lib/std/io/io_file.c3 @@ -6,6 +6,8 @@ fn File! open(String filename, String mode) return { .file = os::native_fopen(filename, mode) }; } +fn void! delete(String filename) => os::native_remove(filename) @inline; + fn File! open_path(Path path, String mode) { return { .file = os::native_fopen(path.as_str(), mode) }; diff --git a/lib/std/io/os/chdir.c3 b/lib/std/io/os/chdir.c3 index 438ce10dc..fa7596364 100644 --- a/lib/std/io/os/chdir.c3 +++ b/lib/std/io/os/chdir.c3 @@ -4,7 +4,7 @@ import libc; macro void! native_chdir(Path path) { $switch - $case POSIX_LIBC: + $case env::POSIX: if (posix::chdir(path.as_zstr())) { switch (libc::errno()) @@ -17,7 +17,7 @@ macro void! native_chdir(Path path) default: return IoError.GENERAL_ERROR?; } } - $case WIN32_LIBC: + $case env::WIN32: @pool() { // TODO improve with better error handling. diff --git a/lib/std/io/os/file.c3 b/lib/std/io/os/file_libc.c3 similarity index 56% rename from lib/std/io/os/file.c3 rename to lib/std/io/os/file_libc.c3 index 77876a297..3b1dd352b 100644 --- a/lib/std/io/os/file.c3 +++ b/lib/std/io/os/file_libc.c3 @@ -1,42 +1,44 @@ -module std::io::os; +module std::io::os @if(env::LIBC); import libc; -def FopenFn = fn void*!(String, String); -def FreopenFn = fn void*!(void*, String, String); -def FcloseFn = fn void!(void*); -def FseekFn = fn void!(void*, isz, Seek); -def FtellFn = fn usz!(void*); -def FwriteFn = fn usz!(void*, char[] buffer); -def FreadFn = fn usz!(void*, char[] buffer); - -FopenFn native_fopen_fn @weak @if(!$defined(native_fopen_fn)); -FcloseFn native_fclose_fn @weak @if(!$defined(native_fclose_fn)); -FreopenFn native_freopen_fn @weak @if(!$defined(native_freopen_fn)); -FseekFn native_fseek_fn @weak @if(!$defined(native_fseek_fn)); -FtellFn native_ftell_fn @weak @if(!$defined(native_ftell_fn)); -FwriteFn native_fwrite_fn @weak @if(!$defined(native_fwrite_fn)); -FreadFn native_fread_fn @weak @if(!$defined(native_fread_fn)); - /** * @require mode.len > 0 * @require filename.len > 0 **/ fn void*! native_fopen(String filename, String mode) @inline { - $if !env::COMPILER_LIBC_AVAILABLE: - if (native_fopen_fn) return native_fopen_fn(filename, mode); - unreachable("Tried to call fopen without support."); - $else - @pool() - { + @pool() + { $if env::WIN32: void* file = libc::_wfopen(filename.to_temp_utf16(), filename.to_temp_utf16())!; $else void* file = libc::fopen(filename.zstr_tcopy(), mode.zstr_tcopy()); $endif return file ?: file_open_errno()?; - }; - $endif + }; +} + +fn void! native_remove(String filename) +{ + @pool() + { + $if env::WIN32: + CInt result = libc::_wremove(filename.to_temp_utf16())!; + $else + CInt result = libc::remove(filename.zstr_tcopy()); + $endif + if (result) + { + switch (libc::errno()) + { + case errno::ENOENT: + return IoError.FILE_NOT_FOUND?; + case errno::EACCES: + default: + return IoError.FILE_CANNOT_DELETE?; + } + } + }; } /** @@ -45,61 +47,37 @@ fn void*! native_fopen(String filename, String mode) @inline **/ fn void*! native_freopen(void* file, String filename, String mode) @inline { - $if !env::COMPILER_LIBC_AVAILABLE: - if (native_freopen_fn) return native_freopen_fn(file, filename, mode); - unreachable("Tried to call freopen without support."); - $else - @pool() - { - $if env::os_is_win32(): + @pool() + { + $if env::WIN32: file = libc::_wfreopen(filename.to_temp_utf16(), mode.to_temp_utf16(), file)!; $else file = libc::freopen(filename.zstr_tcopy(), mode.zstr_tcopy(), file); $endif return file ?: file_open_errno()?; - }; - $endif + }; } fn void! native_fseek(void* file, isz offset, Seek seek_mode) @inline { - $if !env::COMPILER_LIBC_AVAILABLE: - if (native_fseek_fn) return native_fseek_fn(file, offset, seek_mode); - unreachable("Tried to call fseek without support."); - $else - if (libc::fseek(file, (SeekIndex)offset, (CInt)seek_mode)) return file_seek_errno()?; - $endif + if (libc::fseek(file, (SeekIndex)offset, (CInt)seek_mode)) return file_seek_errno()?; } + fn usz! native_ftell(CFile file) @inline { - $if !env::COMPILER_LIBC_AVAILABLE: - if (native_ftell_fn) return native_ftell_fn(file); - unreachable("Tried to call ftell without support."); - $else - long index = libc::ftell(file); - return index >= 0 ? (usz)index : file_seek_errno()?; - $endif + long index = libc::ftell(file); + return index >= 0 ? (usz)index : file_seek_errno()?; } fn usz! native_fwrite(CFile file, char[] buffer) @inline { - $if !env::COMPILER_LIBC_AVAILABLE: - if (native_fwrite_fn) return native_fwrite_fn(file, buffer); - unreachable("Tried to call fwrite without support."); - $else - return libc::fwrite(buffer.ptr, 1, buffer.len, file); - $endif + return libc::fwrite(buffer.ptr, 1, buffer.len, file); } fn usz! native_fread(CFile file, char[] buffer) @inline { - $if !env::COMPILER_LIBC_AVAILABLE: - if (native_fread_fn) return native_fread_fn(file, buffer); - unreachable("Tried to call fread without support."); - $else - return libc::fread(buffer.ptr, 1, buffer.len, file); - $endif + return libc::fread(buffer.ptr, 1, buffer.len, file); } macro anyfault file_open_errno() @local @@ -149,5 +127,3 @@ macro anyfault file_seek_errno() @local } } -// Posix functions -extern fn CInt access(ZString path, CInt mode) @if(POSIX_LIBC); diff --git a/lib/std/io/os/file_nolibc.c3 b/lib/std/io/os/file_nolibc.c3 new file mode 100644 index 000000000..329928c12 --- /dev/null +++ b/lib/std/io/os/file_nolibc.c3 @@ -0,0 +1,75 @@ +module std::io::os @if(env::NO_LIBC); +import libc; + +def FopenFn = fn void*!(String, String); +def FreopenFn = fn void*!(void*, String, String); +def FcloseFn = fn void!(void*); +def FseekFn = fn void!(void*, isz, Seek); +def FtellFn = fn usz!(void*); +def FwriteFn = fn usz!(void*, char[] buffer); +def FreadFn = fn usz!(void*, char[] buffer); +def RemoveFn = fn void!(String); + +FopenFn native_fopen_fn @weak @if(!$defined(native_fopen_fn)); +FcloseFn native_fclose_fn @weak @if(!$defined(native_fclose_fn)); +FreopenFn native_freopen_fn @weak @if(!$defined(native_freopen_fn)); +FseekFn native_fseek_fn @weak @if(!$defined(native_fseek_fn)); +FtellFn native_ftell_fn @weak @if(!$defined(native_ftell_fn)); +FwriteFn native_fwrite_fn @weak @if(!$defined(native_fwrite_fn)); +FreadFn native_fread_fn @weak @if(!$defined(native_fread_fn)); +RemoveFn native_remove_fn @weak @if(!$defined(native_remove_fn)); + +/** + * @require mode.len > 0 + * @require filename.len > 0 + **/ +fn void*! native_fopen(String filename, String mode) @inline +{ + if (native_fopen_fn) return native_fopen_fn(filename, mode); + unreachable("Tried to call fopen without support."); +} + +/** + * Delete a file. + * + * @require filename.len > 0 + **/ +fn void! native_remove(String filename) @inline +{ + if (native_remove_fn) return native_remove_fn(filename); + unreachable("Tried to call remove without support."); +} + +/** + * @require mode.len > 0 + * @require filename.len > 0 + **/ +fn void*! native_freopen(void* file, String filename, String mode) @inline +{ + if (native_freopen_fn) return native_freopen_fn(file, filename, mode); + unreachable("Tried to call freopen without support."); +} + +fn void! native_fseek(void* file, isz offset, Seek seek_mode) @inline +{ + if (native_fseek_fn) return native_fseek_fn(file, offset, seek_mode); + unreachable("Tried to call fseek without support."); +} + +fn usz! native_ftell(CFile file) @inline +{ + if (native_ftell_fn) return native_ftell_fn(file); + unreachable("Tried to call ftell without support."); +} + +fn usz! native_fwrite(CFile file, char[] buffer) @inline +{ + if (native_fwrite_fn) return native_fwrite_fn(file, buffer); + unreachable("Tried to call fwrite without support."); +} + +fn usz! native_fread(CFile file, char[] buffer) @inline +{ + if (native_fread_fn) return native_fread_fn(file, buffer); + unreachable("Tried to call fread without support."); +} diff --git a/lib/std/io/os/fileinfo.c3 b/lib/std/io/os/fileinfo.c3 new file mode 100644 index 000000000..5eb9d7230 --- /dev/null +++ b/lib/std/io/os/fileinfo.c3 @@ -0,0 +1,115 @@ +module std::io::os @if(env::LIBC); +import libc; + +fn void! native_stat(Stat* stat, String path) @if(env::DARWIN || env::LINUX) +{ + @pool() + { + $if env::DARWIN || env::LINUX: + int res = libc::stat(path.zstr_tcopy(), stat); + $else + unreachable("Stat unimplemented"); + int res = 0; + $endif + if (res != 0) + { + switch (libc::errno()) + { + case errno::EBADF: + return IoError.FILE_NOT_VALID?; + case errno::EFAULT: + unreachable("Invalid stat"); + case errno::EIO: + return IoError.GENERAL_ERROR?; + case errno::EACCES: + return IoError.NO_PERMISSION?; + case errno::ELOOP: + return IoError.NO_PERMISSION?; + case errno::ENAMETOOLONG: + return IoError.NAME_TOO_LONG?; + case errno::ENOENT: + return IoError.FILE_NOT_FOUND?; + case errno::ENOTDIR: + return IoError.FILE_NOT_DIR?; + case errno::EOVERFLOW: + return IoError.GENERAL_ERROR?; + default: + return IoError.UNKNOWN_ERROR?; + } + } + }; +} + +fn usz! native_file_size(String path) @if(env::WIN32) +{ + @pool() + { + Char16[] path16 = path.to_temp_utf16()!; + Win32_FILE_ATTRIBUTE_DATA data; + win32::getFileAttributesExW(path16, Win32_GET_FILEEX_INFO_LEVELS.STANDARD, &data); + Win32_LARGE_INTEGER size; + size.lowPart = data.nFileSizeLow; + size.highPart = data.nFileSizeHigh; + return (usz)size.quadPart; + }; +} + +fn usz! native_file_size(String path) @if(!env::WIN32 && !env::DARWIN) +{ + File f = file::open(path, "r")!; + defer (void)f.close(); + return f.seek(0, Seek.END)!; +} + +fn usz! native_file_size(String path) @if(env::DARWIN) +{ + Stat stat; + native_stat(&stat, path)!; + return stat.st_size; +} + +fn bool native_file_or_dir_exists(String path) +{ + $switch + $case env::DARWIN: + $case env::LINUX: + Stat stat; + return @ok(native_stat(&stat, path)); + $case env::WIN32: + @pool() + { + return (bool)win32::pathFileExistsW(path.to_temp_utf16()) ?? false; + }; + $case env::POSIX: + @pool() + { + return posix::access(path.zstr_tcopy(), 0 /* F_OK */) != -1; + }; + $default: + unreachable("Not supported"); + $endswitch +} + +fn bool native_is_file(String path) +{ + $switch + $case env::DARWIN: + $case env::LINUX: + Stat stat; + return @ok(native_stat(&stat, path)) && stat.st_mode & libc::S_IFREG; + $default: + File! f = file::open(path, "r"); + defer (void)f.close(); + return @ok(f); + $endswitch +} + +fn bool native_is_dir(String path) +{ + $if env::DARWIN || env::LINUX: + Stat stat; + return @ok(native_stat(&stat, path)) && stat.st_mode & libc::S_IFDIR; + $else + return native_file_or_dir_exists(path) && !native_is_file(path); + $endif +} diff --git a/lib/std/io/os/fileinfo_darwin.c3 b/lib/std/io/os/fileinfo_darwin.c3 deleted file mode 100644 index e92054e3b..000000000 --- a/lib/std/io/os/fileinfo_darwin.c3 +++ /dev/null @@ -1,98 +0,0 @@ -module std::io::file::os @if(DARWIN_LIBC); -import libc; - -struct DarwinTimespec @private -{ - long tv_sec; - long tv_nsec; -} -struct Darwin64Stat @private -{ - int st_dev; - ushort st_mode; - ushort st_nlink; - ulong st_ino; - uint st_uid; - uint st_gid; - int st_rdev; - DarwinTimespec st_atimespec; // time of last access - DarwinTimespec st_mtimespec; // time of last data modification - DarwinTimespec st_ctimespec; // time of last status change - DarwinTimespec st_birthtimespec; // time of file creation(birth) - long st_size; - long st_blocks; - int st_blocksize; - uint st_flags; - uint st_gen; - int st_lspare; - long[2] st_qspare; -} -extern fn int _stat(ZString str, Darwin64Stat* stat) @extern("stat64"); - -const S_IFMT = 0o170000; // type of file mask -const S_IFIFO = 0o010000; // named pipe (fifo) -const S_IFCHR = 0o020000; // character special -const S_IFDIR = 0o040000; // directory -const S_IFBLK = 0o060000; // block special -const S_IFREG = 0o100000; // regular -const S_IFLNK = 0o120000; // symbolic link -const S_IFSOCK = 0o140000; // socket - -fn usz! native_file_size(String path) -{ - Darwin64Stat stat; - read_stat(&stat, path)!; - return stat.st_size; -} - -fn bool native_file_or_dir_exists(String path) -{ - Darwin64Stat stat; - return @ok(read_stat(&stat, path)); -} - -fn bool native_is_file(String path) -{ - Darwin64Stat stat; - return @ok(read_stat(&stat, path)) && stat.st_mode & S_IFREG; -} - -fn bool native_is_dir(String path) -{ - Darwin64Stat stat; - return @ok(read_stat(&stat, path)) && stat.st_mode & S_IFDIR; -} - -fn void! read_stat(Darwin64Stat* stat, String path) @local -{ - @pool() - { - int res = _stat(path.zstr_tcopy(), stat); - if (res != 0) - { - switch (libc::errno()) - { - case errno::EBADF: - return IoError.FILE_NOT_VALID?; - case errno::EFAULT: - unreachable("Invalid stat"); - case errno::EIO: - return IoError.GENERAL_ERROR?; - case errno::EACCES: - return IoError.NO_PERMISSION?; - case errno::ELOOP: - return IoError.NO_PERMISSION?; - case errno::ENAMETOOLONG: - return IoError.NAME_TOO_LONG?; - case errno::ENOENT: - return IoError.FILE_NOT_FOUND?; - case errno::ENOTDIR: - return IoError.FILE_NOT_DIR?; - case errno::EOVERFLOW: - return IoError.GENERAL_ERROR?; - default: - return IoError.UNKNOWN_ERROR?; - } - } - }; -} \ No newline at end of file diff --git a/lib/std/io/os/fileinfo_other.c3 b/lib/std/io/os/fileinfo_other.c3 deleted file mode 100644 index 84f1419e1..000000000 --- a/lib/std/io/os/fileinfo_other.c3 +++ /dev/null @@ -1,144 +0,0 @@ -module std::io::file::os; - -const USE_DARWIN_INODE64 = env::os_is_darwin() && env::ARCH_TYPE == X86_64; - -extern fn NativeDirentry* readdir(void*) @extern("readdir") @if(!USE_DARWIN_INODE64) ; -extern fn NativeDirentry* readdir(void*) @extern("readdir$INODE64") @if(USE_DARWIN_INODE64); - -struct NativeDirentry -{ - usz ino; - usz seekoff @if(env::os_is_darwin()); - isz seekoff @if(!env::os_is_darwin()); - ushort reclen; - ushort namelen @if(env::os_is_darwin()); - char type; - char[1024] name @if(env::os_is_darwin()); - char[*] name @if(!env::os_is_darwin()); -} - -module std::io::file::os @if(!env::WIN32); - -// native_temp_directory, for non Win32 - -fn Path! native_temp_directory(Allocator* using = mem::heap()) -{ - foreach (String env : { "TMPDIR", "TMP", "TEMP", "TEMPDIR" }) - { - String tmpdir = env::get_var(env) ?? ""; - if (tmpdir) return path::new(tmpdir, using); - } - return path::new("/tmp", using); -} - -module std::io::file::os @if(env::COMPILER_LIBC_AVAILABLE && !env::os_is_win32()); - -extern fn void* opendir(ZString); -extern fn void closedir(void*); -extern fn int remove(ZString); - -const DT_UNKNOWN = 0; -const DT_FIFO = 1; -const DT_CHR = 2; -const DT_DIR = 4; -const DT_BLK = 6; -const DT_REG = 8; -const DT_LNK = 10; -const DT_SOCK = 12; -const DT_WHT = 14; - -fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Allocator* using) -{ - PathList list; - list.init(.using = using); - void* directory = opendir(dir.as_str() ? dir.as_zstr() : (ZString)"."); - defer if (directory) closedir(directory); - if (!directory) return (path::is_dir(dir) ? IoError.CANNOT_READ_DIR : IoError.FILE_NOT_DIR)?; - NativeDirentry* entry; - while ((entry = readdir(directory))) - { - String name = ((ZString)&entry.name).as_str(); - if (!name || name == "." || name == "..") continue; - if (entry.type == DT_LNK && no_symlinks) continue; - if (entry.type == DT_DIR && no_dirs) continue; - Path path = path::new(name.copy(using), using)!!; - list.append(path); - } - return list; -} - -/** - * @require dir.as_str() - **/ -fn void! native_rmtree(Path dir) -{ - void* directory = opendir(dir.as_zstr()); - defer if (directory) closedir(directory); - if (!directory) return path::is_dir(dir) ? IoError.CANNOT_READ_DIR? : IoError.FILE_NOT_DIR?; - NativeDirentry* entry; - while ((entry = readdir(directory))) - { - @pool() - { - String name = ((ZString)&entry.name).as_str(); - if (!name || name == "." || name == "..") continue; - Path new_path = dir.tappend(name)!; - if (entry.type == DT_DIR) - { - native_rmtree(new_path)!; - continue; - } - if (remove(new_path.as_zstr())) - { - // TODO improve - return IoError.GENERAL_ERROR?; - } - }; - } - os::native_rmdir(dir)!; -} - -module std::io::file::os @if(!env::os_is_darwin() && !env::os_is_win32()); - -fn usz! native_file_size(String path) -{ - File f = file::open(path, "r")!; - defer (void)f.close(); - return f.seek(0, Seek.END)!; -} - -fn bool native_file_or_dir_exists(String path) -{ - $if POSIX_LIBC: - @pool() - { - return os::access(path.zstr_tcopy(), 0 /* F_OK */) != -1; - }; - $else - unreachable("Tried to call file_or_dir_exists without support."); - return false; - $endif -} - -fn bool native_is_file(String path) -{ - $if POSIX_LIBC: - File! f = file::open(path, "r"); - defer (void)f.close(); - return @ok(f); - $else - unreachable("Tried to call is_file without support."); - return false; - $endif -} - -fn bool native_is_dir(String path) -{ - $if POSIX_LIBC: - return native_file_or_dir_exists(path) && !native_is_file(path); - $else - unreachable("Tried to call is_dir without support."); - return false; - $endif -} - diff --git a/lib/std/io/os/fileinfo_win32.c3 b/lib/std/io/os/fileinfo_win32.c3 deleted file mode 100644 index fc8ac76cb..000000000 --- a/lib/std/io/os/fileinfo_win32.c3 +++ /dev/null @@ -1,103 +0,0 @@ -module std::io::file::os @if(env::WIN32); -import std::os::win32; - - -fn usz! native_file_size(String path) -{ - @pool() - { - Char16[] path16 = path.to_temp_utf16()!; - Win32_FILE_ATTRIBUTE_DATA data; - win32::getFileAttributesExW(path16, Win32_GET_FILEEX_INFO_LEVELS.STANDARD, &data); - Win32_LARGE_INTEGER size; - size.lowPart = data.nFileSizeLow; - size.highPart = data.nFileSizeHigh; - return (usz)size.quadPart; - }; -} - -fn bool native_file_or_dir_exists(String path) -{ - @pool() - { - return (bool)win32::pathFileExistsW(path.to_temp_utf16()) ?? false; - }; -} - - -fn bool native_is_file(String path) -{ - File! f = file::open(path, "r"); - defer (void)f.close(); - return @ok(f); -} - -fn bool native_is_dir(String path) -{ - return native_file_or_dir_exists(path) && !native_is_file(path); -} - -fn void! native_rmtree(Path path) -{ - Win32_WIN32_FIND_DATAW find_data; - - String s = path.as_str().tconcat("\\*"); - Win32_HANDLE find = win32::findFirstFileW(s.to_utf16(mem::temp()), &find_data)!; - - if (find == win32::INVALID_HANDLE_VALUE) return IoError.CANNOT_READ_DIR?; - - defer win32::findClose(find); - do - { - String filename = string::from_zutf16(&find_data.cFileName, mem::temp())!; - if (filename == "." || filename == "..") continue; - Path file_path = path.tappend(filename)!; - if (find_data.dwFileAttributes & win32::FILE_ATTRIBUTE_DIRECTORY) - { - native_rmtree(file_path)!; - } - else - { - win32::deleteFileW(file_path.as_str().to_utf16(mem::temp())); - } - } while (win32::findNextFileW(find, &find_data) != 0); - os::native_rmdir(path)!; -} - -fn Path! native_temp_directory(Allocator* using = mem::heap()) -{ - @stack_mem(256; Allocator* mem) - { - Win32_DWORD len = win32::getTempPathW(0, null); - if (!len) return IoError.GENERAL_ERROR?; - Char16[] buff = malloc(Char16, len + 1, .using = mem); - if (!win32::getTempPathW(len, buff)) return IoError.GENERAL_ERROR?; - return path::new(string::from_utf16(buff[:len], .using = mem), using); - }; -} - -fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Allocator* using) -{ - PathList list; - list.init(.using = using); - @stack_mem(1024; Allocator* mem) - { - Char16* result = dir.as_str().concat(`\*`).to_utf16(.using = mem)!!; - list.append(path::new("test")); - Win32_WIN32_FIND_DATAW find_data; - Win32_HANDLE find = win32::findFirstFileW(result, &find_data); - if (find == win32::INVALID_HANDLE_VALUE) return IoError.CANNOT_READ_DIR?; - defer win32::findClose(find); - do - { - if (no_dirs && (find_data.dwFileAttributes & win32::FILE_ATTRIBUTE_DIRECTORY)) continue; - @stack_mem(480; Allocator* mem2) - { - String filename = string::from_zutf16(&find_data.cFileName, mem2)!; - if (filename == ".." || filename == ".") continue; - list.append(path::new(filename, using)); - }; - } while (win32::findNextFileW(find, &find_data)); - return list; - }; -} diff --git a/lib/std/io/os/getcwd.c3 b/lib/std/io/os/getcwd.c3 index 97268616e..c74fb6414 100644 --- a/lib/std/io/os/getcwd.c3 +++ b/lib/std/io/os/getcwd.c3 @@ -4,7 +4,7 @@ import libc; macro String! getcwd(Allocator* using = mem::heap()) { $switch - $case WIN32_LIBC: + $case env::WIN32: const DEFAULT_BUFFER = 256; Char16[DEFAULT_BUFFER] buffer; Char16 *res = win32::_wgetcwd(&buffer, DEFAULT_BUFFER); @@ -19,7 +19,7 @@ macro String! getcwd(Allocator* using = mem::heap()) Char16[] str16 = res[:win32::wcslen(res)]; return string::from_utf16(str16, using); - $case POSIX_LIBC: + $case env::POSIX: const usz DEFAULT_BUFFER = 256; char[DEFAULT_BUFFER] buffer; ZString res = posix::getcwd(&buffer, DEFAULT_BUFFER); diff --git a/lib/std/io/os/ls.c3 b/lib/std/io/os/ls.c3 new file mode 100644 index 000000000..f41006d89 --- /dev/null +++ b/lib/std/io/os/ls.c3 @@ -0,0 +1,48 @@ +module std::io::file::os @if(env::POSIX); + +fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Allocator* using) +{ + PathList list; + list.init(.using = using); + DIRPtr directory = posix::opendir(dir.as_str() ? dir.as_zstr() : (ZString)"."); + defer if (directory) posix::closedir(directory); + if (!directory) return (path::is_dir(dir) ? IoError.CANNOT_READ_DIR : IoError.FILE_NOT_DIR)?; + Posix_dirent* entry; + while ((entry = posix::readdir(directory))) + { + String name = ((ZString)&entry.name).as_str(); + if (!name || name == "." || name == "..") continue; + if (entry.d_type == posix::DT_LNK && no_symlinks) continue; + if (entry.d_type == posix::DT_DIR && no_dirs) continue; + Path path = path::new(name.copy(using), using)!!; + list.append(path); + } + return list; +} + +module std::io::os @if(env::WIN32); + +fn PathList! native_ls(Path dir, bool no_dirs, bool no_symlinks, String mask, Allocator* using) +{ + PathList list; + list.init(.using = using); + @stack_mem(1024; Allocator* mem) + { + Char16* result = dir.as_str().concat(`\*`).to_utf16(.using = mem)!!; + Win32_WIN32_FIND_DATAW find_data; + Win32_HANDLE find = win32::findFirstFileW(result, &find_data); + if (find == win32::INVALID_HANDLE_VALUE) return IoError.CANNOT_READ_DIR?; + defer win32::findClose(find); + do + { + if (no_dirs && (find_data.dwFileAttributes & win32::FILE_ATTRIBUTE_DIRECTORY)) continue; + @stack_mem(480; Allocator* mem2) + { + String filename = string::from_zutf16(&find_data.cFileName, mem2)!; + if (filename == ".." || filename == ".") continue; + list.append(path::new(filename, using)); + }; + } while (win32::findNextFileW(find, &find_data)); + return list; + }; +} diff --git a/lib/std/io/os/mkdir.c3 b/lib/std/io/os/mkdir.c3 index 1ffa7f05b..3a95ad59c 100644 --- a/lib/std/io/os/mkdir.c3 +++ b/lib/std/io/os/mkdir.c3 @@ -8,7 +8,7 @@ import std::os::posix; macro bool! native_mkdir(Path path, MkdirPermissions permissions) { $switch - $case POSIX_LIBC: + $case env::POSIX: if (!posix::mkdir(path.as_zstr(), permissions == NORMAL ? 0o777 : 0o700)) return true; switch (libc::errno()) { @@ -25,7 +25,7 @@ macro bool! native_mkdir(Path path, MkdirPermissions permissions) case errno::ENOTDIR: return IoError.FILE_NOT_FOUND?; default: return IoError.GENERAL_ERROR?; } - $case WIN32_LIBC: + $case env::WIN32: @pool() { // TODO security attributes diff --git a/lib/std/io/os/rmdir.c3 b/lib/std/io/os/rmdir.c3 index 76c47d44d..9c8bf5257 100644 --- a/lib/std/io/os/rmdir.c3 +++ b/lib/std/io/os/rmdir.c3 @@ -7,7 +7,7 @@ import std::os::posix; macro bool! native_rmdir(Path path) { $switch - $case POSIX_LIBC: + $case env::POSIX: if (!posix::rmdir(path.as_zstr())) return true; switch (libc::errno()) { @@ -23,7 +23,7 @@ macro bool! native_rmdir(Path path) case errno::ELOOP: return IoError.SYMLINK_FAILED?; default: return IoError.GENERAL_ERROR?; } - $case WIN32_LIBC: + $case env::WIN32: @pool() { if (win32::removeDirectoryW(path.as_str().to_temp_utf16()!!)) return true; diff --git a/lib/std/io/os/rmtree.c3 b/lib/std/io/os/rmtree.c3 new file mode 100644 index 000000000..7849285dd --- /dev/null +++ b/lib/std/io/os/rmtree.c3 @@ -0,0 +1,60 @@ +module std::io::file::os @if(env::POSIX); +import libc; + +/** + * @require dir.as_str() + **/ +fn void! native_rmtree(Path dir) +{ + DIRPtr directory = posix::opendir(dir.as_zstr()); + defer if (directory) posix::closedir(directory); + if (!directory) return path::is_dir(dir) ? IoError.CANNOT_READ_DIR? : IoError.FILE_NOT_DIR?; + Posix_dirent* entry; + while ((entry = posix::readdir(directory))) + { + @pool() + { + String name = ((ZString)&entry.name).as_str(); + if (!name || name == "." || name == "..") continue; + Path new_path = dir.tappend(name)!; + if (entry.d_type == posix::DT_DIR) + { + native_rmtree(new_path)!; + continue; + } + if (libc::remove(new_path.as_zstr())) + { + // TODO improve + return IoError.GENERAL_ERROR?; + } + }; + } + os::native_rmdir(dir)!; +} + +module std::io::os @if(env::WIN32); + +fn void! native_rmtree(Path path) +{ + Win32_WIN32_FIND_DATAW find_data; + String s = path.as_str().tconcat("\\*"); + Win32_HANDLE find = win32::findFirstFileW(s.to_utf16(mem::temp()), &find_data)!; + + if (find == win32::INVALID_HANDLE_VALUE) return IoError.CANNOT_READ_DIR?; + defer win32::findClose(find); + do + { + String filename = string::from_zutf16(&find_data.cFileName, mem::temp())!; + if (filename == "." || filename == "..") continue; + Path file_path = path.tappend(filename)!; + if (find_data.dwFileAttributes & win32::FILE_ATTRIBUTE_DIRECTORY) + { + native_rmtree(file_path)!; + } + else + { + win32::deleteFileW(file_path.as_str().to_utf16(mem::temp())); + } + } while (win32::findNextFileW(find, &find_data) != 0); + os::native_rmdir(path)!; +} diff --git a/lib/std/io/os/temp_directory.c3 b/lib/std/io/os/temp_directory.c3 new file mode 100644 index 000000000..023d55109 --- /dev/null +++ b/lib/std/io/os/temp_directory.c3 @@ -0,0 +1,30 @@ +module std::io::os @if(env::LIBC); + +fn Path! native_temp_directory(Allocator* using = mem::heap()) @if(!env::WIN32) +{ + foreach (String env : { "TMPDIR", "TMP", "TEMP", "TEMPDIR" }) + { + String tmpdir = env::get_var(env) ?? ""; + if (tmpdir) return path::new(tmpdir, using); + } + return path::new("/tmp", using); +} + +fn Path! native_temp_directory(Allocator* using = mem::heap()) @if(env::WIN32) +{ + @stack_mem(256; Allocator* mem) + { + Win32_DWORD len = win32::getTempPathW(0, null); + if (!len) return IoError.GENERAL_ERROR?; + Char16[] buff = malloc(Char16, len + 1, .using = mem); + if (!win32::getTempPathW(len, buff)) return IoError.GENERAL_ERROR?; + return path::new(string::from_utf16(buff[:len], .using = mem), using); + }; +} + +module std::io::os @if(env::NO_LIBC); + +macro Path! native_temp_directory(Allocator* using = mem::heap()) +{ + unreachable("Not available"); +} diff --git a/lib/std/io/path.c3 b/lib/std/io/path.c3 index 72c677033..2dc483519 100644 --- a/lib/std/io/path.c3 +++ b/lib/std/io/path.c3 @@ -1,10 +1,10 @@ module std::io::path; import std::collections::list; -const PathEnv DEFAULT_PATH_ENV = env::os_is_win32() ? PathEnv.WIN32 : PathEnv.POSIX; +const PathEnv DEFAULT_PATH_ENV = env::WIN32 ? PathEnv.WIN32 : PathEnv.POSIX; const char PREFERRED_SEPARATOR_WIN32 = '\\'; const char PREFERRED_SEPARATOR_POSIX = '/'; -const char PREFERRED_SEPARATOR = env::os_is_win32() ? PREFERRED_SEPARATOR_WIN32 : PREFERRED_SEPARATOR_POSIX; +const char PREFERRED_SEPARATOR = env::WIN32 ? PREFERRED_SEPARATOR_WIN32 : PREFERRED_SEPARATOR_POSIX; def PathList = List; @@ -42,6 +42,7 @@ fn bool exists(Path path) => os::native_file_or_dir_exists(path.as_str()); fn Path! tgetcwd() => getcwd(mem::temp()) @inline; fn void! chdir(Path path) => os::native_chdir(path) @inline; fn Path! temp_directory(Allocator* using = mem::heap()) => os::native_temp_directory(using); +fn void! delete(Path path) => os::native_remove(path.as_str()) @inline; macro bool is_separator(char c, PathEnv path_env = DEFAULT_PATH_ENV) { @@ -60,7 +61,11 @@ macro bool is_win32_separator(char c) fn PathList! ls(Path dir, bool no_dirs = false, bool no_symlinks = false, String mask = "", Allocator* using = mem::heap()) { - return os::native_ls(dir, no_dirs, no_symlinks, mask, using); + $if $defined(os::native_ls): + return os::native_ls(dir, no_dirs, no_symlinks, mask, using); + $else + unreachable("'ls' is not available."); + $endif } enum MkdirPermissions diff --git a/lib/std/libc/libc.c3 b/lib/std/libc/libc.c3 index a06092930..a27d4b67c 100644 --- a/lib/std/libc/libc.c3 +++ b/lib/std/libc/libc.c3 @@ -38,9 +38,16 @@ def JmpBuf = uptr[$$JMP_BUF_SIZE]; def Fd = CInt; def Fpos_t = long; // TODO make sure fpos is correct on all targets. def SignalFunction = fn void(int); +def Time_t = $typefrom(env::WIN32 ? long.typeid : CLong.typeid); +def Off_t = $typefrom(env::WIN32 ? int.typeid : usz.typeid); -module libc @if(env::COMPILER_LIBC_AVAILABLE); +struct Timespec +{ + Time_t tv_sec; + CLong tv_nsec; +} +module libc @if(env::LIBC); extern fn void abort(); extern fn CInt abs(CInt n); @@ -85,7 +92,6 @@ extern fn ZString getenv(ZString name); extern fn ZString gets(char* buffer); extern fn Tm* gmtime(Time_t* timer); extern fn Tm* gmtime_r(Time_t *timer, Tm* buf) @if(!env::WIN32); - extern fn CLong labs(CLong x); extern fn LongDivResult ldiv(CLong number, CLong denom); extern fn Tm* localtime(Time_t* timer); @@ -152,7 +158,6 @@ extern fn CInt ungetc(CInt c, CFile stream); extern fn CInt unsetenv(ZString name); extern fn isz write(Fd fd, void* buffer, usz count) @if(!env::WIN32); - extern fn CFile fmemopen(void* ptr, usz size, ZString mode); extern fn isz getline(char** linep, usz* linecapp, CFile stream); extern fn int timespec_get(TimeSpec* ts, int base); @@ -164,7 +169,7 @@ const CInt STDIN_FD = 0; const CInt STDOUT_FD = 1; const CInt STDERR_FD = 2; -module libc @if(LINUX_LIBC); +module libc @if(env::LINUX); extern CFile __stdin @extern("stdin"); extern CFile __stdout @extern("stdout"); extern CFile __stderr @extern("stderr"); @@ -175,7 +180,7 @@ macro CFile stdin() => __stdin; macro CFile stdout() => __stdout; macro CFile stderr() => __stderr; -module libc @if(DARWIN_LIBC); +module libc @if(env::DARWIN); extern CFile __stdinp; extern CFile __stdoutp; extern CFile __stderrp; @@ -185,18 +190,18 @@ macro CFile stdin() => __stdinp; macro CFile stdout() => __stdoutp; macro CFile stderr() => __stderrp; -module libc @if(WIN32_LIBC); +module libc @if(env::WIN32); macro usz malloc_size(void* ptr) => _msize(ptr); macro CFile stdin() => __acrt_iob_func(STDIN_FD); macro CFile stdout() => __acrt_iob_func(STDOUT_FD); macro CFile stderr() => __acrt_iob_func(STDERR_FD); -module libc @if(env::COMPILER_LIBC_AVAILABLE && !env::WIN32 && !env::LINUX && !env::DARWIN); +module libc @if(env::LIBC && !env::WIN32 && !env::LINUX && !env::DARWIN); macro CFile stdin() { return (CFile*)(uptr)STDIN_FD; } macro CFile stdout() { return (CFile*)(uptr)STDOUT_FD; } macro CFile stderr() { return (CFile*)(uptr)STDERR_FD; } -module libc @if(!env::COMPILER_LIBC_AVAILABLE); +module libc @if(!env::LIBC); fn void longjmp(JmpBuf* buffer, CInt value) @weak @extern("longjmp") @nostrip { @@ -317,10 +322,7 @@ module libc; def CFile = void*; -const HAS_MALLOC_SIZE = - env::OS_TYPE == LINUX - || env::os_is_win32() - || env::os_is_darwin(); +const HAS_MALLOC_SIZE = env::LINUX || env::WIN32 || env::DARWIN; // The following needs to be set per arch+os // For now I have simply pulled the defaults from MacOS @@ -335,6 +337,21 @@ const int EOF = -1; const int FOPEN_MAX = 20; const int FILENAME_MAX = 1024; +const S_IFMT = 0o170000; // type of file mask +const S_IFIFO = 0o010000; // named pipe (fifo) +const S_IFCHR = 0o020000; // character special +const S_IFDIR = 0o040000; // directory +const S_IFBLK = 0o060000; // block special +const S_IFREG = 0o100000; // regular +const S_IFLNK = 0o120000; // symbolic link +const S_IFSOCK = 0o140000; // socket +const S_ISUID = 0o004000; // Set user id on execution +const S_ISGID = 0o002000; // Set group id on execution +const S_ISVTX = 0o001000; // Save swapped text even after use +const S_IRUSR = 0o000400; // Read permission, owner +const S_IWUSR = 0o000200; // Write permission, owner +const S_IXUSR = 0o000100; // Execute/search permission, owner + def SeekIndex = CLong; // vsprintf vprintf not supported @@ -343,19 +360,18 @@ def SeekIndex = CLong; struct Tm { - int tm_sec; /* seconds after the minute [0-60] */ - int tm_min; /* minutes after the hour [0-59] */ - int tm_hour; /* hours since midnight [0-23] */ - int tm_mday; /* day of the month [1-31] */ - int tm_mon; /* months since January [0-11] */ - int tm_year; /* years since 1900 */ - int tm_wday; /* days since Sunday [0-6] */ - int tm_yday; /* days since January 1 [0-365] */ - int tm_isdst; /* Daylight Savings Time flag */ + CInt tm_sec; // seconds after the minute [0-60] + CInt tm_min; // minutes after the hour [0-59] + CInt tm_hour; // hours since midnight [0-23] + CInt tm_mday; // day of the month [1-31] + CInt tm_mon; // months since January [0-11] + CInt tm_year; // years since 1900 + CInt tm_wday; // days since Sunday [0-6] + CInt tm_yday; // days since January 1 [0-365] + CInt tm_isdst; // Daylight Savings Time flag TimeOffset tm_gmtoff @if(!env::WIN32); /* offset from UTC in seconds */ char *tm_zone @if(!env::WIN32); /* timezone abbreviation */ - int tm_nsec @if(env::WASI); - + CInt tm_nsec @if(env::WASI); } struct TimeSpec @@ -365,9 +381,8 @@ struct TimeSpec CLong ns @if(!env::WIN32); } -def Time_t = long @if(env::WIN32); -def Time_t = CLong @if(!env::WIN32); -def Clock_t = ulong @if(env::WIN32); + +def Clock_t = int @if(env::WIN32); def Clock_t = CULong @if(!env::WIN32); def TimeOffset = int @if(env::WASI) ; diff --git a/lib/std/libc/os/darwin.c3 b/lib/std/libc/os/darwin.c3 new file mode 100644 index 000000000..0a8df7f8f --- /dev/null +++ b/lib/std/libc/os/darwin.c3 @@ -0,0 +1,33 @@ +module libc @if(env::DARWIN); + +def Dev_t = int; +def Mode_t = ushort; +def Nlink_t = ushort; +def Blkcnt_t = long; +def Blksize_t = int; +def Ino_t = ulong; + +struct Stat +{ + Dev_t st_dev; + Mode_t st_mode; + Nlink_t st_nlink; + Ino_t st_ino; + Uid_t st_uid; + Gid_t st_gid; + Dev_t st_rdev; + + Timespec st_atimespec; // time of last access + Timespec st_mtimespec; // time of last data modification + Timespec st_ctimespec; // time of last status change + Timespec st_birthtimespec; // time of file creation(birth) + Off_t st_size; // file size, in bytes + Blkcnt_t st_blocks; // blocks allocated for file + Blksize_t st_blocksize; // optimal blocksize for I/O + uint st_flags; // user defined flags for file + uint st_gen; // file generation number + int st_lspare; // RESERVED + long[2] st_qspare; // RESERVED +} + +extern fn int stat(ZString str, Stat* stat) @extern("stat64"); diff --git a/lib/std/libc/os/errno.c3 b/lib/std/libc/os/errno.c3 index 47fef8719..66679fb01 100644 --- a/lib/std/libc/os/errno.c3 +++ b/lib/std/libc/os/errno.c3 @@ -1,28 +1,28 @@ module libc::os; // Linux -extern fn int* __errno_location() @if(LINUX_LIBC); -macro int errno() @if(LINUX_LIBC) => *__errno_location(); -macro void errno_set(int err) @if(LINUX_LIBC) => *(__errno_location()) = err; +extern fn int* __errno_location() @if(env::LINUX); +macro int errno() @if(env::LINUX) => *__errno_location(); +macro void errno_set(int err) @if(env::LINUX) => *(__errno_location()) = err; // Darwin -extern fn int* __error() @if(DARWIN_LIBC); -macro int errno() @if(DARWIN_LIBC) => *__error(); -macro void errno_set(int err) @if(DARWIN_LIBC) => *(__error()) = err; +extern fn int* __error() @if(env::DARWIN); +macro int errno() @if(env::DARWIN) => *__error(); +macro void errno_set(int err) @if(env::DARWIN) => *(__error()) = err; // Win32 -macro int errno() @if(WIN32_LIBC) +macro int errno() @if(env::WIN32) { int holder; _get_errno(&holder); return holder; } -macro void errno_set(int err) @if(WIN32_LIBC) => _set_errno(err); -extern fn void _get_errno(int* result) @if(WIN32_LIBC); -extern fn void _set_errno(int err) @if(WIN32_LIBC); +macro void errno_set(int err) @if(env::WIN32) => _set_errno(err); +extern fn void _get_errno(int* result) @if(env::WIN32); +extern fn void _set_errno(int err) @if(env::WIN32); // Default -const ERRNO_DEFAULT @local = !LINUX_LIBC && !DARWIN_LIBC && !WIN32_LIBC; +const ERRNO_DEFAULT @local = !env::LINUX && !env::DARWIN && !env::WIN32; tlocal int _errno_c3 @if(ERRNO_DEFAULT) = 0; fn void errno_set(int err) @if(ERRNO_DEFAULT) => _errno_c3 = err; fn int errno() @if(ERRNO_DEFAULT) => _errno_c3; \ No newline at end of file diff --git a/lib/std/libc/os/linux.c3 b/lib/std/libc/os/linux.c3 new file mode 100644 index 000000000..0d428002b --- /dev/null +++ b/lib/std/libc/os/linux.c3 @@ -0,0 +1,59 @@ +module libc @if(env::LINUX); + +// Checked for x86_64, this is notoriously incorrect when comparing with Rust code etc + +def Blksize_t = $typefrom(env::X86_64 ? long.typeid : CInt.typeid); +def Nlink_t = $typefrom(env::X86_64 ? ulong.typeid : CUInt.typeid); +def Blkcnt_t = long; +def Ino_t = ulong; +def Dev_t = ulong; +def Mode_t = uint; +def Ino64_t = ulong; +def Blkcnt64_t = long; + +struct Stat @if(env::X86_64) +{ + Dev_t st_dev; + Ino_t st_ino; + Nlink_t st_nlink; + Mode_t st_mode; + Uid_t st_uid; + Gid_t st_gid; + CInt __pad0; + Dev_t st_rdev; + Off_t st_size; + Blksize_t st_blksize; + Blkcnt_t st_blocks; + Time_t st_atime; + long st_atime_nsec; + Time_t st_mtime; + long st_mtime_nsec; + Time_t st_ctime; + long st_ctime_nsec; + long[3] __unused; +} + +struct Stat @if(!env::X86_64) +{ + Dev_t st_dev; + Ino_t st_ino; + Mode_t st_mode; + Nlink_t st_nlink; + Uid_t st_uid; + Gid_t st_gid; + Dev_t st_rdev; + CInt __pad1; + Off_t st_size; + Blksize_t st_blksize; + CInt __pad2; + Blkcnt_t st_blocks; + Time_t st_atime; + long st_atime_nsec; + Time_t st_mtime; + long st_mtime_nsec; + Time_t st_ctime; + long st_ctime_nsec; + CInt[2] __unused; +} + +extern fn CInt stat(ZString path, Stat* stat); diff --git a/lib/std/libc/os/posix.c3 b/lib/std/libc/os/posix.c3 new file mode 100644 index 000000000..fe14982d7 --- /dev/null +++ b/lib/std/libc/os/posix.c3 @@ -0,0 +1,5 @@ +module libc @if(env::POSIX); + +def Pid_t = int; +def Uid_t = uint; +def Gid_t = uint; diff --git a/lib/std/libc/os/win32.c3 b/lib/std/libc/os/win32.c3 index bd8152f58..549206663 100644 --- a/lib/std/libc/os/win32.c3 +++ b/lib/std/libc/os/win32.c3 @@ -1,5 +1,6 @@ module libc @if(env::WIN32); + extern fn CFile __acrt_iob_func(CInt c); extern fn CInt _close(Fd fd); def close = _close; extern fn double _difftime64(Time_t time1, Time_t time2); def difftime = _difftime64; @@ -18,10 +19,11 @@ extern fn CInt _setjmp(void* frameptr, JmpBuf* buffer) @if(env::WIN32); extern fn CFile _wfopen(Char16*, Char16*); extern fn CFile _wfreopen(Char16*, Char16*, CFile); extern fn CInt _write(Fd fd, void* buffer, CUInt count); +extern fn CInt _wremove(Char16*); // Aliases to simplify libc use macro Tm* localtime_r(Time_t* timer, Tm* buf) => _localtime64_s(buf, timer); -macro CInt setjmp(JmpBuf* buffer) @if(env::WIN32) => _setjmp($$frameaddress(), buffer); +macro CInt setjmp(JmpBuf* buffer) => _setjmp($$frameaddress(), buffer); macro Tm* gmtime_r(Time_t* timer, Tm* buf) => _gmtime64_s(buf, timer); macro isz read(Fd fd, void* buffer, usz buffer_size) => _read(fd, buffer, buffer_size); macro isz write(Fd fd, void* buffer, usz count) => _write(fd, buffer, count); diff --git a/lib/std/os/macos/cf_allocator.c3 b/lib/std/os/macos/cf_allocator.c3 index 2cb45a63b..c43ef2c90 100644 --- a/lib/std/os/macos/cf_allocator.c3 +++ b/lib/std/os/macos/cf_allocator.c3 @@ -1,4 +1,4 @@ -module std::os::macos::cf @if(DARWIN_LIBC); +module std::os::macos::cf @if(env::DARWIN); def CFAllocatorRef = distinct void*; def CFAllocatorContextRef = distinct void*; diff --git a/lib/std/os/macos/cf_array.c3 b/lib/std/os/macos/cf_array.c3 index a4fc9fbe2..912024610 100644 --- a/lib/std/os/macos/cf_array.c3 +++ b/lib/std/os/macos/cf_array.c3 @@ -1,4 +1,4 @@ -module std::os::macos::cf @if(DARWIN_LIBC); +module std::os::macos::cf @if(env::DARWIN); def CFArrayRef = distinct void*; def CFArrayCallBacksRef = distinct void*; diff --git a/lib/std/os/macos/core_foundation.c3 b/lib/std/os/macos/core_foundation.c3 index 1700a8021..435f04ce9 100644 --- a/lib/std/os/macos/core_foundation.c3 +++ b/lib/std/os/macos/core_foundation.c3 @@ -1,4 +1,4 @@ -module std::os::macos::cf @if(DARWIN_LIBC); +module std::os::macos::cf @if(env::DARWIN); def CFTypeRef = distinct void*; def CFIndex = isz; diff --git a/lib/std/os/macos/objc.c3 b/lib/std/os/macos/objc.c3 index 9813f53a9..eb3c56e1d 100644 --- a/lib/std/os/macos/objc.c3 +++ b/lib/std/os/macos/objc.c3 @@ -1,4 +1,4 @@ -module std::os::macos::objc @if(DARWIN_LIBC); +module std::os::macos::objc @if(env::DARWIN); def Class = distinct void*; def Method = distinct void*; diff --git a/lib/std/os/posix/files.c3 b/lib/std/os/posix/files.c3 index f2c994087..f3e38b297 100644 --- a/lib/std/os/posix/files.c3 +++ b/lib/std/os/posix/files.c3 @@ -1,11 +1,72 @@ -module std::os::posix @if(POSIX_LIBC); +module std::os::posix @if(env::POSIX); import libc; +def Mode_t = uint; +def DIRPtr = distinct void*; + +struct DarwinTimespec @private +{ + long tv_sec; + long tv_nsec; +} + +struct Darwin64Stat @private +{ + int st_dev; + ushort st_mode; + ushort st_nlink; + ulong st_ino; + uint st_uid; + uint st_gid; + int st_rdev; + DarwinTimespec st_atimespec; // time of last access + DarwinTimespec st_mtimespec; // time of last data modification + DarwinTimespec st_ctimespec; // time of last status change + DarwinTimespec st_birthtimespec; // time of file creation(birth) + long st_size; + long st_blocks; + int st_blocksize; + uint st_flags; + uint st_gen; + int st_lspare; + long[2] st_qspare; +} + +struct Posix_dirent +{ + Ino_t d_fileno; + Off_t d_off; + ushort d_reclen; + ushort d_namelen @if(env::DARWIN); + char d_type; + char d_namelen @if(env::FREEBSD || env::OPENBSD); + uint d_pad0 @if(env::FREEBSD); + char[4] d_pad0 @if(env::OPENBSD); + char[1024] name @if(env::DARWIN); + char[*] name @if(!env::DARWIN); +} + extern fn int rmdir(ZString); extern fn int mkdir(ZString, ushort mode_t); extern fn int chdir(ZString); extern fn ZString getcwd(char* pwd, usz len); -def Mode_t = uint; -def Pid_t = int; extern fn CInt pipe(CInt[2]* pipes); extern fn CFile fdopen(CInt fd, ZString mode); +extern fn CInt access(ZString path, CInt mode); +extern fn Posix_dirent* readdir(DIRPtr) @extern("readdir") @if(!USE_DARWIN_INODE64) ; +extern fn DIRPtr opendir(ZString); +extern fn void closedir(DIRPtr); + +const DT_UNKNOWN = 0; +const DT_FIFO = 1; +const DT_CHR = 2; +const DT_DIR = 4; +const DT_BLK = 6; +const DT_REG = 8; +const DT_LNK = 10; +const DT_SOCK = 12; +const DT_WHT = 14; + +const USE_DARWIN_INODE64 = env::DARWIN && env::ARCH_TYPE == X86_64; +extern fn Posix_dirent* readdir(DIRPtr) @extern("readdir$INODE64") @if(USE_DARWIN_INODE64); + diff --git a/lib/std/os/posix/process.c3 b/lib/std/os/posix/process.c3 index 487dedaae..f1c145dcc 100644 --- a/lib/std/os/posix/process.c3 +++ b/lib/std/os/posix/process.c3 @@ -1,5 +1,5 @@ -module std::os::posix @if(POSIX_LIBC); - +module std::os::posix @if(env::POSIX); +import libc; struct Posix_spawn_file_actions_t { int __allocated; diff --git a/lib/std/os/win32/files.c3 b/lib/std/os/win32/files.c3 index a8bb621fc..e0c1a1517 100644 --- a/lib/std/os/win32/files.c3 +++ b/lib/std/os/win32/files.c3 @@ -1,4 +1,4 @@ -module std::os::win32 @if(WIN32_LIBC); +module std::os::win32 @if(env::WIN32); import libc; enum Win32_GET_FILEEX_INFO_LEVELS { @@ -96,7 +96,8 @@ extern fn usz wcslen(Char16* str); extern fn int _open_osfhandle(iptr osfhandle, int flags); extern fn iptr _get_osfhandle(int fd); extern fn CFile _fdopen(int fd, ZString mode); - +extern fn CInt _access(ZString path, CInt mode); +extern fn CInt _waccess(Char16* path, CInt mode); /* extern ulong _win32_GetCurrentDirectoryW(ulong, Char16* buffer) @extern("GetCurrentDirectoryW"); diff --git a/lib/std/os/win32/general.c3 b/lib/std/os/win32/general.c3 index 7f1a636cd..9fd6b193c 100644 --- a/lib/std/os/win32/general.c3 +++ b/lib/std/os/win32/general.c3 @@ -1,4 +1,4 @@ -module std::os::win32 @if(WIN32_LIBC); +module std::os::win32 @if(env::WIN32); extern fn Win32_DWORD getLastError() @extern("GetLastError"); diff --git a/lib/std/os/win32/process.c3 b/lib/std/os/win32/process.c3 index 6db272116..8be5561bb 100644 --- a/lib/std/os/win32/process.c3 +++ b/lib/std/os/win32/process.c3 @@ -1,4 +1,4 @@ -module std::os::win32 @if(WIN32_LIBC); +module std::os::win32 @if(env::WIN32); const Win32_DWORD STARTF_USESTDHANDLES = 0x00000100; const Win32_DWORD CREATE_NO_WINDOW = 0x08000000; diff --git a/lib/std/os/win32/wsa.c3 b/lib/std/os/win32/wsa.c3 index 5498ffe12..121bbe2af 100644 --- a/lib/std/os/win32/wsa.c3 +++ b/lib/std/os/win32/wsa.c3 @@ -1,4 +1,4 @@ -module std::os::win32 @if(WIN32_LIBC); +module std::os::win32 @if(env::WIN32); def WSAError = distinct int; diff --git a/lib/std/threads/thread.c3 b/lib/std/threads/thread.c3 index b0bdc0a3c..b120a85e8 100644 --- a/lib/std/threads/thread.c3 +++ b/lib/std/threads/thread.c3 @@ -7,8 +7,8 @@ enum ThreadModel NONE } -const ThreadModel THREAD_MODEL = env::COMPILER_LIBC_AVAILABLE - ? (env::os_is_win32() ? ThreadModel.WIN32 : ThreadModel.POSIX) +const ThreadModel THREAD_MODEL = env::LIBC + ? (env::WIN32 ? ThreadModel.WIN32 : ThreadModel.POSIX) : ThreadModel.NONE; def MutexType = distinct int; diff --git a/lib/std/time/datetime.c3 b/lib/std/time/datetime.c3 index 2fa546aed..4619078dd 100644 --- a/lib/std/time/datetime.c3 +++ b/lib/std/time/datetime.c3 @@ -1,4 +1,4 @@ -module std::time::datetime; +module std::time::datetime @if(env::LIBC); import libc; fn DateTime now() diff --git a/lib/std/time/os/time_darwin.c3 b/lib/std/time/os/time_darwin.c3 index 8aafe6360..049640e43 100644 --- a/lib/std/time/os/time_darwin.c3 +++ b/lib/std/time/os/time_darwin.c3 @@ -1,4 +1,4 @@ -module std::time::os @if(DARWIN_LIBC); +module std::time::os @if(env::DARWIN); struct Darwin_mach_timebase_info { diff --git a/lib/std/time/os/time_posix.c3 b/lib/std/time/os/time_posix.c3 index 4a1f09f7a..4c56a0811 100644 --- a/lib/std/time/os/time_posix.c3 +++ b/lib/std/time/os/time_posix.c3 @@ -1,4 +1,4 @@ -module std::time::os @if(POSIX_LIBC); +module std::time::os @if(env::POSIX); import libc; extern fn void clock_gettime(int type, TimeSpec *time); @@ -10,14 +10,14 @@ fn Time native_timestamp() return (Time)(ts.s * 1_000_000i64 + ts.ns / 1_000i64); } -fn Clock native_clock() @if(!DARWIN_LIBC) +fn Clock native_clock() @if(!env::DARWIN) { TimeSpec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (Clock)((ulong)ts.s * 1_000_000_000u64 + (ulong)ts.ns); } -module std::time::os @if(OPENBSD_LIBC); +module std::time::os @if(env::OPENBSD); const CLOCK_REALTIME = 0; const CLOCK_PROCESS_CPUTIME_ID = 2; const CLOCK_MONOTONIC = 3; @@ -25,7 +25,7 @@ const CLOCK_THREAD_CPUTIME_ID = 4; const CLOCK_UPTIME = 5; const CLOCK_BOOTTIME = 6; -module std::time::os @if(FREEBSD_LIBC); +module std::time::os @if(env::FREEBSD); const CLOCK_REALTIME = 0; const CLOCK_VIRTUAL = 1; const CLOCK_PROF = 2; @@ -44,7 +44,7 @@ const CLOCK_BOOTTIME = CLOCK_UPTIME; const CLOCK_REALTIME_COARSE = CLOCK_REALTIME_FAST; const CLOCK_MONOTONIC_COARSE = CLOCK_MONOTONIC_FAST; -module std::time::os @if(NETBSD_LIBC); +module std::time::os @if(env::NETBSD); const CLOCK_REALTIME = 0; const CLOCK_VIRTUAL = 1; const CLOCK_PROF = 2; @@ -52,12 +52,12 @@ const CLOCK_MONOTONIC = 3; const CLOCK_THREAD_CPUTIME_ID = 0x20000000; const CLOCK_PROCESS_CPUTIME_ID = 0x40000000; -module std::time::os @if(WASI_LIBC); +module std::time::os @if(env::WASI); // Not implemented const CLOCK_REALTIME = 0; const CLOCK_MONOTONIC = 0; -module std::time::os @if(DARWIN_LIBC); +module std::time::os @if(env::DARWIN); const CLOCK_REALTIME = 0; const CLOCK_MONOTONIC = 6; const CLOCK_MONOTONIC_RAW = 4; @@ -67,7 +67,7 @@ const CLOCK_UPTIME_RAW_APPROX = 9; const CLOCK_PROCESS_CPUTIME_ID = 12; const CLOCK_THREAD_CPUTIME_ID = 16; -module std::time::os @if(LINUX_LIBC); +module std::time::os @if(env::LINUX); const CLOCK_REALTIME = 0; const CLOCK_MONOTONIC = 1; const CLOCK_PROCESS_CPUTIME_ID = 2; diff --git a/lib/std/time/os/time_win32.c3 b/lib/std/time/os/time_win32.c3 index fbe0f8c3f..212ff59c4 100644 --- a/lib/std/time/os/time_win32.c3 +++ b/lib/std/time/os/time_win32.c3 @@ -1,4 +1,4 @@ -module std::time::os @if(WIN32_LIBC); +module std::time::os @if(env::WIN32); import std::os::win32; extern fn void win32_GetSystemTimeAsFileTime(Win32_FILETIME* time) @extern("GetSystemTimeAsFileTime"); diff --git a/src/version.h b/src/version.h index f1bf3f667..69f43f2d8 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.4.537" \ No newline at end of file +#define COMPILER_VERSION "0.4.538" \ No newline at end of file