diff --git a/core/compress/gzip/doc.odin b/core/compress/gzip/doc.odin index 82eaa6f35..e4b1929dd 100644 --- a/core/compress/gzip/doc.odin +++ b/core/compress/gzip/doc.odin @@ -2,11 +2,11 @@ A small `GZIP` unpacker. Example: - import "core:bytes" - import os "core:os/os2" - import "core:compress" - import "core:compress/gzip" - import "core:fmt" + import "core:bytes" + import "core:os" + import "core:compress" + import "core:compress/gzip" + import "core:fmt" // Small GZIP file with fextra, fname and fcomment present. @private diff --git a/core/compress/gzip/gzip.odin b/core/compress/gzip/gzip.odin index 644a625e7..aedbe3a83 100644 --- a/core/compress/gzip/gzip.odin +++ b/core/compress/gzip/gzip.odin @@ -14,12 +14,12 @@ package compress_gzip to be the input to a complementary TAR implementation. */ -import "core:compress/zlib" -import "core:compress" -import os "core:os/os2" -import "core:io" -import "core:bytes" -import "core:hash" +import "core:compress/zlib" +import "core:compress" +import "core:os" +import "core:io" +import "core:bytes" +import "core:hash" Magic :: enum u16le { GZIP = 0x8b << 8 | 0x1f, diff --git a/core/crypto/hash/hash_os.odin b/core/crypto/hash/hash_os.odin index 5155623cb..49c1a0ff8 100644 --- a/core/crypto/hash/hash_os.odin +++ b/core/crypto/hash/hash_os.odin @@ -3,7 +3,7 @@ package crypto_hash import "core:io" -import os "core:os/os2" +import "core:os" // `hash_file` will read the file provided by the given handle and return the // computed digest in a newly allocated slice. diff --git a/core/encoding/csv/doc.odin b/core/encoding/csv/doc.odin index c6dae3005..1fb685602 100644 --- a/core/encoding/csv/doc.odin +++ b/core/encoding/csv/doc.odin @@ -6,7 +6,7 @@ Example: import "core:fmt" import "core:encoding/csv" - import os "core:os/os2" + import "core:os" // Requires keeping the entire CSV file in memory at once iterate_csv_from_string :: proc(filename: string) { diff --git a/core/encoding/hxa/hxa_os.odin b/core/encoding/hxa/hxa_os.odin index c033bdca8..17ad94819 100644 --- a/core/encoding/hxa/hxa_os.odin +++ b/core/encoding/hxa/hxa_os.odin @@ -2,7 +2,7 @@ #+build !js package encoding_hxa -import os "core:os/os2" +import "core:os" read_from_file :: proc(filename: string, print_error := false, allocator := context.allocator, loc := #caller_location) -> (file: File, err: Read_Error) { context.allocator = allocator diff --git a/core/encoding/ini/ini_os.odin b/core/encoding/ini/ini_os.odin index 619a0e2a6..22c6bf7b3 100644 --- a/core/encoding/ini/ini_os.odin +++ b/core/encoding/ini/ini_os.odin @@ -3,7 +3,7 @@ package encoding_ini import "base:runtime" -import os "core:os/os2" +import "core:os" load_map_from_path :: proc(path: string, allocator: runtime.Allocator, options := DEFAULT_OPTIONS) -> (m: Map, err: runtime.Allocator_Error, ok: bool) { data, data_err := os.read_entire_file(path, allocator) diff --git a/core/encoding/xml/xml_os.odin b/core/encoding/xml/xml_os.odin index 8c7f6cccf..1e94572c6 100644 --- a/core/encoding/xml/xml_os.odin +++ b/core/encoding/xml/xml_os.odin @@ -2,7 +2,7 @@ #+build !js package encoding_xml -import os "core:os/os2" +import "core:os" // Load an XML file load_from_file :: proc(filename: string, options := DEFAULT_OPTIONS, error_handler := default_error_handler, allocator := context.allocator) -> (doc: ^Document, err: Error) { diff --git a/core/flags/errors.odin b/core/flags/errors.odin index d0caa1427..efe4cb6c4 100644 --- a/core/flags/errors.odin +++ b/core/flags/errors.odin @@ -2,7 +2,7 @@ package flags import "base:runtime" import "core:net" -import os "core:os/os2" +import "core:os" Parse_Error_Reason :: enum { None, diff --git a/core/flags/example/example.odin b/core/flags/example/example.odin index 6e74c7dcc..6ace3d852 100644 --- a/core/flags/example/example.odin +++ b/core/flags/example/example.odin @@ -4,7 +4,7 @@ import "base:runtime" import "core:flags" import "core:fmt" import "core:net" -import os "core:os/os2" +import "core:os" import "core:time/datetime" diff --git a/core/flags/internal_rtti.odin b/core/flags/internal_rtti.odin index 07481a89b..d5e8726e2 100644 --- a/core/flags/internal_rtti.odin +++ b/core/flags/internal_rtti.odin @@ -1,18 +1,18 @@ #+private package flags -import "base:intrinsics" -import "base:runtime" -import "core:fmt" -import "core:mem" -import "core:net" -@(require) import os "core:os/os2" -import "core:reflect" -import "core:strconv" -import "core:strings" -@require import "core:time" -@require import "core:time/datetime" -import "core:unicode/utf8" +import "base:intrinsics" +import "base:runtime" +import "core:fmt" +import "core:mem" +import "core:net" +@(require) import "core:os" +import "core:reflect" +import "core:strconv" +import "core:strings" +@(require) import "core:time" +@(require) import "core:time/datetime" +import "core:unicode/utf8" @(optimization_mode="favor_size") parse_and_set_pointer_by_base_type :: proc(ptr: rawptr, str: string, type_info: ^runtime.Type_Info) -> bool { diff --git a/core/flags/internal_validation.odin b/core/flags/internal_validation.odin index dc19f3084..6f9016a21 100644 --- a/core/flags/internal_validation.odin +++ b/core/flags/internal_validation.odin @@ -1,14 +1,14 @@ #+private package flags -@require import "base:runtime" -@require import "core:container/bit_array" -@require import "core:fmt" -@require import "core:mem" -@require import os "core:os/os2" -@require import "core:reflect" -@require import "core:strconv" -@require import "core:strings" +@require import "base:runtime" +@require import "core:container/bit_array" +@require import "core:fmt" +@require import "core:mem" +@require import "core:os" +@require import "core:reflect" +@require import "core:strconv" +@require import "core:strings" // This proc is used to assert that `T` meets the expectations of the library. @(optimization_mode="favor_size", disabled=ODIN_DISABLE_ASSERT) diff --git a/core/flags/util.odin b/core/flags/util.odin index 20e40cab5..0d18fa196 100644 --- a/core/flags/util.odin +++ b/core/flags/util.odin @@ -1,9 +1,9 @@ package flags -import "core:fmt" -@require import os "core:os/os2" +import "core:fmt" +@require import "core:os" @require import "core:path/filepath" -import "core:strings" +import "core:strings" /* Parse any arguments into an annotated struct or exit if there was an error. diff --git a/core/fmt/fmt_os.odin b/core/fmt/fmt_os.odin index 7ce945a0f..0305b5bac 100644 --- a/core/fmt/fmt_os.odin +++ b/core/fmt/fmt_os.odin @@ -3,10 +3,10 @@ #+build !orca package fmt -import "base:runtime" -import os "core:os/os2" -import "core:io" -import "core:bufio" +import "base:runtime" +import "core:os" +import "core:io" +import "core:bufio" // NOTE(Jeroen): The other option is to deprecate `fprint*` and make it an alias for `wprint*`, using File.stream directly. diff --git a/core/image/bmp/bmp_os.odin b/core/image/bmp/bmp_os.odin index 971750fda..1aa1d63de 100644 --- a/core/image/bmp/bmp_os.odin +++ b/core/image/bmp/bmp_os.odin @@ -1,8 +1,8 @@ #+build !js package core_image_bmp -import os "core:os/os2" -import "core:bytes" +import "core:os" +import "core:bytes" load :: proc{load_from_file, load_from_bytes, load_from_context} diff --git a/core/image/general_os.odin b/core/image/general_os.odin index e4de1c9a6..63d7c8d43 100644 --- a/core/image/general_os.odin +++ b/core/image/general_os.odin @@ -1,7 +1,7 @@ #+build !js package image -import os "core:os/os2" +import "core:os" load :: proc{ load_from_bytes, diff --git a/core/image/jpeg/jpeg_os.odin b/core/image/jpeg/jpeg_os.odin index aad172c91..6ba301d80 100644 --- a/core/image/jpeg/jpeg_os.odin +++ b/core/image/jpeg/jpeg_os.odin @@ -1,7 +1,7 @@ #+build !js package jpeg -import os "core:os/os2" +import "core:os" load :: proc{load_from_file, load_from_bytes, load_from_context} diff --git a/core/image/netpbm/netpbm_os.odin b/core/image/netpbm/netpbm_os.odin index 82ad55f35..ae9029b54 100644 --- a/core/image/netpbm/netpbm_os.odin +++ b/core/image/netpbm/netpbm_os.odin @@ -1,7 +1,7 @@ #+build !js package netpbm -import os "core:os/os2" +import "core:os" load :: proc { load_from_file, diff --git a/core/image/png/png_os.odin b/core/image/png/png_os.odin index c6a88fa52..5fc10cec4 100644 --- a/core/image/png/png_os.odin +++ b/core/image/png/png_os.odin @@ -1,7 +1,7 @@ #+build !js package png -import os "core:os/os2" +import "core:os" load :: proc{load_from_file, load_from_bytes, load_from_context} diff --git a/core/image/qoi/qoi_os.odin b/core/image/qoi/qoi_os.odin index a65527d09..f2bf83cfc 100644 --- a/core/image/qoi/qoi_os.odin +++ b/core/image/qoi/qoi_os.odin @@ -1,8 +1,8 @@ #+build !js package qoi -import os "core:os/os2" -import "core:bytes" +import "core:os" +import "core:bytes" load :: proc{load_from_file, load_from_bytes, load_from_context} diff --git a/core/image/tga/tga_os.odin b/core/image/tga/tga_os.odin index 2c103b34a..ba50439de 100644 --- a/core/image/tga/tga_os.odin +++ b/core/image/tga/tga_os.odin @@ -1,8 +1,8 @@ #+build !js package tga -import os "core:os/os2" -import "core:bytes" +import "core:os" +import "core:bytes" load :: proc{load_from_file, load_from_bytes, load_from_context} diff --git a/core/log/file_console_logger.odin b/core/log/file_console_logger.odin index 819d494e9..47174719f 100644 --- a/core/log/file_console_logger.odin +++ b/core/log/file_console_logger.odin @@ -3,13 +3,13 @@ #+build !js package log -import "base:runtime" -import "core:fmt" -import "core:strings" -import os "core:os/os2" -import "core:terminal" -import "core:terminal/ansi" -import "core:time" +import "base:runtime" +import "core:fmt" +import "core:strings" +import "core:os" +import "core:terminal" +import "core:terminal/ansi" +import "core:time" Level_Headers := [?]string{ 0..<10 = "[DEBUG] --- ", diff --git a/core/math/big/radix_os.odin b/core/math/big/radix_os.odin index 8269a4338..50454b679 100644 --- a/core/math/big/radix_os.odin +++ b/core/math/big/radix_os.odin @@ -18,7 +18,7 @@ package math_big */ import "core:mem" -import os "core:os/os2" +import "core:os" /* We might add functions to read and write byte-encoded Ints from/to files, using `int_to_bytes_*` functions. diff --git a/core/mem/virtual/doc.odin b/core/mem/virtual/doc.odin index 249e22ee8..b5f0944c7 100644 --- a/core/mem/virtual/doc.odin +++ b/core/mem/virtual/doc.odin @@ -5,8 +5,8 @@ virtual.Arena usage Example: // Source: https://github.com/odin-lang/examples/blob/master/arena_allocator/arena_allocator.odin - import "core:fmt" - import os "core:os/os2" + import "core:fmt" + import "core:os" // virtual package implements a multi-purpose arena allocator. If you are on a // platform that does not support virtual memory, then there is also a similar diff --git a/core/mem/virtual/file.odin b/core/mem/virtual/file.odin index b156f2af4..660210bbf 100644 --- a/core/mem/virtual/file.odin +++ b/core/mem/virtual/file.odin @@ -2,7 +2,7 @@ #+build !js package mem_virtual -import os "core:os/os2" +import "core:os" map_file :: proc{ map_file_from_path, diff --git a/core/net/dns_os.odin b/core/net/dns_os.odin index 8528dad00..ad9724d37 100644 --- a/core/net/dns_os.odin +++ b/core/net/dns_os.odin @@ -2,7 +2,7 @@ #+private package net -import os "core:os/os2" +import "core:os" load_resolv_conf :: proc(resolv_conf_path: string, allocator := context.allocator) -> (name_servers: []Endpoint, ok: bool) { context.allocator = allocator diff --git a/core/odin/parser/parse_files.odin b/core/odin/parser/parse_files.odin index 93c282d35..2ea47ca89 100644 --- a/core/odin/parser/parse_files.odin +++ b/core/odin/parser/parse_files.odin @@ -1,12 +1,12 @@ package odin_parser -import "core:odin/tokenizer" -import "core:odin/ast" -import "core:path/filepath" -import "core:fmt" -import os "core:os/os2" -import "core:slice" -import "core:strings" +import "core:odin/tokenizer" +import "core:odin/ast" +import "core:path/filepath" +import "core:fmt" +import "core:os" +import "core:slice" +import "core:strings" collect_package :: proc(path: string) -> (pkg: ^ast.Package, success: bool) { NO_POS :: tokenizer.Pos{} diff --git a/core/os/os2/allocators.odin b/core/os/allocators.odin similarity index 100% rename from core/os/os2/allocators.odin rename to core/os/allocators.odin diff --git a/core/os/os2/dir.odin b/core/os/dir.odin similarity index 99% rename from core/os/os2/dir.odin rename to core/os/dir.odin index f63754273..9ad5f451e 100644 --- a/core/os/os2/dir.odin +++ b/core/os/dir.odin @@ -160,8 +160,8 @@ extend its lifetime. Example: package main - import "core:fmt" - import os "core:os/os2" + import "core:fmt" + import "core:os" main :: proc() { f, oerr := os.open("core") diff --git a/core/os/os2/dir_js.odin b/core/os/dir_js.odin similarity index 100% rename from core/os/os2/dir_js.odin rename to core/os/dir_js.odin diff --git a/core/os/os2/dir_linux.odin b/core/os/dir_linux.odin similarity index 100% rename from core/os/os2/dir_linux.odin rename to core/os/dir_linux.odin diff --git a/core/os/os2/dir_posix.odin b/core/os/dir_posix.odin similarity index 100% rename from core/os/os2/dir_posix.odin rename to core/os/dir_posix.odin diff --git a/core/os/os2/dir_posix_darwin.odin b/core/os/dir_posix_darwin.odin similarity index 100% rename from core/os/os2/dir_posix_darwin.odin rename to core/os/dir_posix_darwin.odin diff --git a/core/os/os2/dir_walker.odin b/core/os/dir_walker.odin similarity index 98% rename from core/os/os2/dir_walker.odin rename to core/os/dir_walker.odin index ba5342cf8..4dce884a8 100644 --- a/core/os/os2/dir_walker.odin +++ b/core/os/dir_walker.odin @@ -136,9 +136,9 @@ If an error occurred opening a directory, you may get zero'd info struct and Example: package main - import "core:fmt" - import "core:strings" - import os "core:os/os2" + import "core:fmt" + import "core:strings" + import "core:os" main :: proc() { w := os.walker_create("core") diff --git a/core/os/os2/dir_wasi.odin b/core/os/dir_wasi.odin similarity index 100% rename from core/os/os2/dir_wasi.odin rename to core/os/dir_wasi.odin diff --git a/core/os/dir_windows.odin b/core/os/dir_windows.odin index 40f4b9e9b..a4dadca75 100644 --- a/core/os/dir_windows.odin +++ b/core/os/dir_windows.odin @@ -1,114 +1,144 @@ -package os +#+private +package os2 -import win32 "core:sys/windows" -import "core:strings" import "base:runtime" +import "core:time" +import win32 "core:sys/windows" + +@(private="file") +find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) { + // Ignore "." and ".." + if d.cFileName[0] == '.' && d.cFileName[1] == 0 { + return + } + if d.cFileName[0] == '.' && d.cFileName[1] == '.' && d.cFileName[2] == 0 { + return + } + + temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) + path := concatenate({base_path, `\`, win32_wstring_to_utf8(cstring16(raw_data(d.cFileName[:])), temp_allocator) or_else ""}, allocator) or_return + + handle := win32.HANDLE(_open_internal(path, {.Read}, Permissions_Read_Write_All) or_else 0) + defer win32.CloseHandle(handle) + + fi.fullpath = path + fi.name = basename(path) + fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow) + + fi.type, fi.mode = _file_type_mode_from_file_attributes(d.dwFileAttributes, handle, d.dwReserved0) + + fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime)) + fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime)) + fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime)) + + if file_id_info: win32.FILE_ID_INFO; handle != nil && win32.GetFileInformationByHandleEx(handle, .FileIdInfo, &file_id_info, size_of(file_id_info)) { + #assert(size_of(fi.inode) == size_of(file_id_info.FileId)) + #assert(size_of(fi.inode) == 16) + runtime.mem_copy_non_overlapping(&fi.inode, &file_id_info.FileId, 16) + } + + return +} + +Read_Directory_Iterator_Impl :: struct { + find_data: win32.WIN32_FIND_DATAW, + find_handle: win32.HANDLE, + path: string, + prev_fi: File_Info, + no_more_files: bool, +} + @(require_results) -read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Error) { - @(require_results) - find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW) -> (fi: File_Info) { - // Ignore "." and ".." - if d.cFileName[0] == '.' && d.cFileName[1] == 0 { +_read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) { + for !it.impl.no_more_files { + err: Error + file_info_delete(it.impl.prev_fi, file_allocator()) + it.impl.prev_fi = {} + + fi, err = find_data_to_file_info(it.impl.path, &it.impl.find_data, file_allocator()) + if err != nil { + read_directory_iterator_set_error(it, it.impl.path, err) return } - if d.cFileName[0] == '.' && d.cFileName[1] == '.' && d.cFileName[2] == 0 { - return - } - path := strings.concatenate({base_path, `\`, win32.utf16_to_utf8(d.cFileName[:]) or_else ""}) - fi.fullpath = path - fi.name = basename(path) - fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow) - if d.dwFileAttributes & win32.FILE_ATTRIBUTE_READONLY != 0 { - fi.mode |= 0o444 - } else { - fi.mode |= 0o666 + if fi.name != "" { + it.impl.prev_fi = fi + ok = true + index = it.index + it.index += 1 } - is_sym := false - if d.dwFileAttributes & win32.FILE_ATTRIBUTE_REPARSE_Point == 0 { - is_sym = false - } else { - is_sym = d.dwReserved0 == win32.IO_REPARSE_TAG_SYMLINK || d.dwReserved0 == win32.IO_REPARSE_TAG_MOUNT_POINT - } - - if is_sym { - fi.mode |= File_Mode_Sym_Link - } else { - if d.dwFileAttributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 { - fi.mode |= 0o111 | File_Mode_Dir + if !win32.FindNextFileW(it.impl.find_handle, &it.impl.find_data) { + e := _get_platform_error() + if pe, _ := is_platform_error(e); pe != i32(win32.ERROR_NO_MORE_FILES) { + read_directory_iterator_set_error(it, it.impl.path, e) } - - // fi.mode |= file_type_mode(h); + it.impl.no_more_files = true } + if ok { + return + } + } + return +} - windows_set_file_info_times(&fi, d) +_read_directory_iterator_init :: proc(it: ^Read_Directory_Iterator, f: ^File) { + it.impl.no_more_files = false - fi.is_dir = fi.mode & File_Mode_Dir != 0 + if f == nil || f.impl == nil { + read_directory_iterator_set_error(it, "", .Invalid_File) return } - if fd == 0 { - return nil, ERROR_INVALID_HANDLE + it.f = f + impl := (^File_Impl)(f.impl) + + // NOTE: Allow calling `init` to target a new directory with the same iterator - reset idx. + if it.impl.find_handle != nil { + win32.FindClose(it.impl.find_handle) + } + if it.impl.path != "" { + delete(it.impl.path, file_allocator()) } - context.allocator = allocator - - h := win32.HANDLE(fd) - - dir_fi, _ := file_info_from_get_file_information_by_handle("", h) - if !dir_fi.is_dir { - return nil, .Not_Dir - } - - n := n - size := n - if n <= 0 { - n = -1 - size = 100 - } - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) - - wpath := cleanpath_from_handle_u16(fd, context.temp_allocator) or_return - if len(wpath) == 0 { + if !is_directory(impl.name) { + read_directory_iterator_set_error(it, impl.name, .Invalid_Dir) return } - dfi := make([dynamic]File_Info, 0, size) or_return + wpath := string16(impl.wname) + temp_allocator := TEMP_ALLOCATOR_GUARD({}) - wpath_search := make([]u16, len(wpath)+3, context.temp_allocator) or_return + wpath_search := make([]u16, len(wpath)+3, temp_allocator) copy(wpath_search, wpath) wpath_search[len(wpath)+0] = '\\' wpath_search[len(wpath)+1] = '*' wpath_search[len(wpath)+2] = 0 - path := cleanpath_from_buf(wpath) - defer delete(path) - - find_data := &win32.WIN32_FIND_DATAW{} - find_handle := win32.FindFirstFileW(cstring16(raw_data(wpath_search)), find_data) - if find_handle == win32.INVALID_HANDLE_VALUE { - err = get_last_error() - return dfi[:], err + it.impl.find_handle = win32.FindFirstFileW(cstring16(raw_data(wpath_search)), &it.impl.find_data) + if it.impl.find_handle == win32.INVALID_HANDLE_VALUE { + read_directory_iterator_set_error(it, impl.name, _get_platform_error()) + return } - defer win32.FindClose(find_handle) - for n != 0 { - fi: File_Info - fi = find_data_to_file_info(path, find_data) - if fi.name != "" { - append(&dfi, fi) - n -= 1 - } - - if !win32.FindNextFileW(find_handle, find_data) { - e := get_last_error() - if e == ERROR_NO_MORE_FILES { - break - } - return dfi[:], e - } + defer if it.err.err != nil { + win32.FindClose(it.impl.find_handle) } - return dfi[:], nil + err: Error + it.impl.path, err = _cleanpath_from_buf(wpath, file_allocator()) + if err != nil { + read_directory_iterator_set_error(it, impl.name, err) + } + + return +} + +_read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) { + if it.f == nil { + return + } + file_info_delete(it.impl.prev_fi, file_allocator()) + delete(it.impl.path, file_allocator()) + win32.FindClose(it.impl.find_handle) } diff --git a/core/os/os2/doc.odin b/core/os/doc.odin similarity index 100% rename from core/os/os2/doc.odin rename to core/os/doc.odin diff --git a/core/os/os2/env.odin b/core/os/env.odin similarity index 100% rename from core/os/os2/env.odin rename to core/os/env.odin diff --git a/core/os/os2/env_js.odin b/core/os/env_js.odin similarity index 100% rename from core/os/os2/env_js.odin rename to core/os/env_js.odin diff --git a/core/os/os2/env_linux.odin b/core/os/env_linux.odin similarity index 100% rename from core/os/os2/env_linux.odin rename to core/os/env_linux.odin diff --git a/core/os/os2/env_posix.odin b/core/os/env_posix.odin similarity index 100% rename from core/os/os2/env_posix.odin rename to core/os/env_posix.odin diff --git a/core/os/os2/env_wasi.odin b/core/os/env_wasi.odin similarity index 100% rename from core/os/os2/env_wasi.odin rename to core/os/env_wasi.odin diff --git a/core/os/env_windows.odin b/core/os/env_windows.odin index ef658b0a1..d389f8860 100644 --- a/core/os/env_windows.odin +++ b/core/os/env_windows.odin @@ -1,30 +1,37 @@ -package os +#+private +package os2 import win32 "core:sys/windows" import "base:runtime" -// lookup_env gets the value of the environment variable named by the key -// If the variable is found in the environment the value (which can be empty) is returned and the boolean is true -// Otherwise the returned value will be empty and the boolean will be false -// NOTE: the value will be allocated with the supplied allocator -@(require_results) -lookup_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { +_lookup_env_alloc :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) { if key == "" { return } - wkey := win32.utf8_to_wstring(key) - n := win32.GetEnvironmentVariableW(wkey, nil, 0) - if n == 0 && get_last_error() == ERROR_ENVVAR_NOT_FOUND { - return "", false - } - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) + temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) + wkey, _ := win32_utf8_to_wstring(key, temp_allocator) + + n := win32.GetEnvironmentVariableW(wkey, nil, 0) + if n == 0 { + err := win32.GetLastError() + if err == win32.ERROR_ENVVAR_NOT_FOUND { + return "", false + } + return "", true + } + + b := make([]u16, n+1, temp_allocator) - b, _ := make([dynamic]u16, n, context.temp_allocator) n = win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b))) - if n == 0 && get_last_error() == ERROR_ENVVAR_NOT_FOUND { + if n == 0 { + err := win32.GetLastError() + if err == win32.ERROR_ENVVAR_NOT_FOUND { + return "", false + } return "", false } - value, _ = win32.utf16_to_utf8(b[:n], allocator) + + value = win32_utf16_to_utf8(string16(b[:n]), allocator) or_else "" found = true return } @@ -33,7 +40,7 @@ lookup_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: // Note that it is limited to environment names and values of 512 utf-16 values each // due to the necessary utf-8 <> utf-16 conversion. @(require_results) -lookup_env_buffer :: proc(buf: []u8, key: string) -> (value: string, err: Error) { +_lookup_env_buf :: proc(buf: []u8, key: string) -> (value: string, err: Error) { key_buf: [513]u16 wkey := win32.utf8_to_wstring(key_buf[:], key) if wkey == nil { @@ -57,78 +64,28 @@ lookup_env_buffer :: proc(buf: []u8, key: string) -> (value: string, err: Error) return value, nil } -lookup_env :: proc{lookup_env_alloc, lookup_env_buffer} +_lookup_env :: proc{_lookup_env_alloc, _lookup_env_buf} -// get_env retrieves the value of the environment variable named by the key -// It returns the value, which will be empty if the variable is not present -// To distinguish between an empty value and an unset value, use lookup_env -// NOTE: the value will be allocated with the supplied allocator -@(require_results) -get_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string) { - value, _ = lookup_env(key, allocator) - return -} - -@(require_results) -get_env_buf :: proc(buf: []u8, key: string) -> (value: string) { - value, _ = lookup_env(buf, key) - return -} -get_env :: proc{get_env_alloc, get_env_buf} - - -// set_env sets the value of the environment variable named by the key -set_env :: proc(key, value: string) -> Error { - k := win32.utf8_to_wstring(key) - v := win32.utf8_to_wstring(value) +_set_env :: proc(key, value: string) -> Error { + temp_allocator := TEMP_ALLOCATOR_GUARD({}) + k := win32_utf8_to_wstring(key, temp_allocator) or_return + v := win32_utf8_to_wstring(value, temp_allocator) or_return if !win32.SetEnvironmentVariableW(k, v) { - return get_last_error() + return _get_platform_error() } return nil } -// unset_env unsets a single environment variable -unset_env :: proc(key: string) -> Error { - k := win32.utf8_to_wstring(key) - if !win32.SetEnvironmentVariableW(k, nil) { - return get_last_error() - } - return nil +_unset_env :: proc(key: string) -> bool { + temp_allocator := TEMP_ALLOCATOR_GUARD({}) + k, _ := win32_utf8_to_wstring(key, temp_allocator) + return bool(win32.SetEnvironmentVariableW(k, nil)) } -// environ returns a copy of strings representing the environment, in the form "key=value" -// NOTE: the slice of strings and the strings with be allocated using the supplied allocator -@(require_results) -environ :: proc(allocator := context.allocator) -> []string { - envs := ([^]win32.WCHAR)(win32.GetEnvironmentStringsW()) - if envs == nil { - return nil - } - defer win32.FreeEnvironmentStringsW(envs) - - r, err := make([dynamic]string, 0, 50, allocator) - if err != nil { - return nil - } - for from, i := 0, 0; true; i += 1 { - if c := envs[i]; c == 0 { - if i <= from { - break - } - append(&r, win32.utf16_to_utf8(envs[from:i], allocator) or_else "") - from = i + 1 - } - } - - return r[:] -} - - -// clear_env deletes all environment variables -clear_env :: proc() { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() - envs := environ(context.temp_allocator) +_clear_env :: proc() { + temp_allocator := TEMP_ALLOCATOR_GUARD({}) + envs, _ := environ(temp_allocator) for env in envs { for j in 1.. string where intrinsics.type_is_enum(Platform_Error) { - if e == nil { - return "" - } - when ODIN_OS == .Darwin { - if s := string(_darwin_string_error(i32(e))); s != "" { - return s - } - } - - when ODIN_OS != .Linux { - @(require_results) - binary_search :: proc "contextless" (array: $A/[]$T, key: T) -> (index: int, found: bool) #no_bounds_check { - n := len(array) - left, right := 0, n - for left < right { - mid := int(uint(left+right) >> 1) - if array[mid] < key { - left = mid+1 - } else { - // equal or greater - right = mid - } - } - return left, left < n && array[left] == key - } - - err := runtime.Type_Info_Enum_Value(e) - - ti := &runtime.type_info_base(type_info_of(Platform_Error)).variant.(runtime.Type_Info_Enum) - if idx, ok := binary_search(ti.values, err); ok { - return ti.names[idx] - } - } else { - @(rodata, static) - pe_strings := [Platform_Error]string{ - .NONE = "", - .EPERM = "Operation not permitted", - .ENOENT = "No such file or directory", - .ESRCH = "No such process", - .EINTR = "Interrupted system call", - .EIO = "Input/output error", - .ENXIO = "No such device or address", - .E2BIG = "Argument list too long", - .ENOEXEC = "Exec format error", - .EBADF = "Bad file descriptor", - .ECHILD = "No child processes", - .EAGAIN = "Resource temporarily unavailable", - .ENOMEM = "Cannot allocate memory", - .EACCES = "Permission denied", - .EFAULT = "Bad address", - .ENOTBLK = "Block device required", - .EBUSY = "Device or resource busy", - .EEXIST = "File exists", - .EXDEV = "Invalid cross-device link", - .ENODEV = "No such device", - .ENOTDIR = "Not a directory", - .EISDIR = "Is a directory", - .EINVAL = "Invalid argument", - .ENFILE = "Too many open files in system", - .EMFILE = "Too many open files", - .ENOTTY = "Inappropriate ioctl for device", - .ETXTBSY = "Text file busy", - .EFBIG = "File too large", - .ENOSPC = "No space left on device", - .ESPIPE = "Illegal seek", - .EROFS = "Read-only file system", - .EMLINK = "Too many links", - .EPIPE = "Broken pipe", - .EDOM = "Numerical argument out of domain", - .ERANGE = "Numerical result out of range", - .EDEADLK = "Resource deadlock avoided", - .ENAMETOOLONG = "File name too long", - .ENOLCK = "No locks available", - .ENOSYS = "Function not implemented", - .ENOTEMPTY = "Directory not empty", - .ELOOP = "Too many levels of symbolic links", - .EUNKNOWN_41 = "Unknown Error (41)", - .ENOMSG = "No message of desired type", - .EIDRM = "Identifier removed", - .ECHRNG = "Channel number out of range", - .EL2NSYNC = "Level 2 not synchronized", - .EL3HLT = "Level 3 halted", - .EL3RST = "Level 3 reset", - .ELNRNG = "Link number out of range", - .EUNATCH = "Protocol driver not attached", - .ENOCSI = "No CSI structure available", - .EL2HLT = "Level 2 halted", - .EBADE = "Invalid exchange", - .EBADR = "Invalid request descriptor", - .EXFULL = "Exchange full", - .ENOANO = "No anode", - .EBADRQC = "Invalid request code", - .EBADSLT = "Invalid slot", - .EUNKNOWN_58 = "Unknown Error (58)", - .EBFONT = "Bad font file format", - .ENOSTR = "Device not a stream", - .ENODATA = "No data available", - .ETIME = "Timer expired", - .ENOSR = "Out of streams resources", - .ENONET = "Machine is not on the network", - .ENOPKG = "Package not installed", - .EREMOTE = "Object is remote", - .ENOLINK = "Link has been severed", - .EADV = "Advertise error", - .ESRMNT = "Srmount error", - .ECOMM = "Communication error on send", - .EPROTO = "Protocol error", - .EMULTIHOP = "Multihop attempted", - .EDOTDOT = "RFS specific error", - .EBADMSG = "Bad message", - .EOVERFLOW = "Value too large for defined data type", - .ENOTUNIQ = "Name not unique on network", - .EBADFD = "File descriptor in bad state", - .EREMCHG = "Remote address changed", - .ELIBACC = "Can not access a needed shared library", - .ELIBBAD = "Accessing a corrupted shared library", - .ELIBSCN = ".lib section in a.out corrupted", - .ELIBMAX = "Attempting to link in too many shared libraries", - .ELIBEXEC = "Cannot exec a shared library directly", - .EILSEQ = "Invalid or incomplete multibyte or wide character", - .ERESTART = "Interrupted system call should be restarted", - .ESTRPIPE = "Streams pipe error", - .EUSERS = "Too many users", - .ENOTSOCK = "Socket operation on non-socket", - .EDESTADDRREQ = "Destination address required", - .EMSGSIZE = "Message too long", - .EPROTOTYPE = "Protocol wrong type for socket", - .ENOPROTOOPT = "Protocol not available", - .EPROTONOSUPPORT = "Protocol not supported", - .ESOCKTNOSUPPORT = "Socket type not supported", - .EOPNOTSUPP = "Operation not supported", - .EPFNOSUPPORT = "Protocol family not supported", - .EAFNOSUPPORT = "Address family not supported by protocol", - .EADDRINUSE = "Address already in use", - .EADDRNOTAVAIL = "Cannot assign requested address", - .ENETDOWN = "Network is down", - .ENETUNREACH = "Network is unreachable", - .ENETRESET = "Network dropped connection on reset", - .ECONNABORTED = "Software caused connection abort", - .ECONNRESET = "Connection reset by peer", - .ENOBUFS = "No buffer space available", - .EISCONN = "Transport endpoint is already connected", - .ENOTCONN = "Transport endpoint is not connected", - .ESHUTDOWN = "Cannot send after transport endpoint shutdown", - .ETOOMANYREFS = "Too many references: cannot splice", - .ETIMEDOUT = "Connection timed out", - .ECONNREFUSED = "Connection refused", - .EHOSTDOWN = "Host is down", - .EHOSTUNREACH = "No route to host", - .EALREADY = "Operation already in progress", - .EINPROGRESS = "Operation now in progress", - .ESTALE = "Stale file handle", - .EUCLEAN = "Structure needs cleaning", - .ENOTNAM = "Not a XENIX named type file", - .ENAVAIL = "No XENIX semaphores available", - .EISNAM = "Is a named type file", - .EREMOTEIO = "Remote I/O error", - .EDQUOT = "Disk quota exceeded", - .ENOMEDIUM = "No medium found", - .EMEDIUMTYPE = "Wrong medium type", - .ECANCELED = "Operation canceled", - .ENOKEY = "Required key not available", - .EKEYEXPIRED = "Key has expired", - .EKEYREVOKED = "Key has been revoked", - .EKEYREJECTED = "Key was rejected by service", - .EOWNERDEAD = "Owner died", - .ENOTRECOVERABLE = "State not recoverable", - .ERFKILL = "Operation not possible due to RF-kill", - .EHWPOISON = "Memory page has hardware error", - } - if Platform_Error.NONE <= e && e <= max(Platform_Error) { - return pe_strings[e] - } - } - return "" -} - -@(private, require_results) +// Attempts to convert an `Error` `ferr` into an `io.Error` +@(private) error_to_io_error :: proc(ferr: Error) -> io.Error { if ferr == nil { return .None diff --git a/core/os/os2/errors_js.odin b/core/os/errors_js.odin similarity index 100% rename from core/os/os2/errors_js.odin rename to core/os/errors_js.odin diff --git a/core/os/os2/errors_linux.odin b/core/os/errors_linux.odin similarity index 100% rename from core/os/os2/errors_linux.odin rename to core/os/errors_linux.odin diff --git a/core/os/os2/errors_posix.odin b/core/os/errors_posix.odin similarity index 100% rename from core/os/os2/errors_posix.odin rename to core/os/errors_posix.odin diff --git a/core/os/os2/errors_wasi.odin b/core/os/errors_wasi.odin similarity index 100% rename from core/os/os2/errors_wasi.odin rename to core/os/errors_wasi.odin diff --git a/core/os/os2/errors_windows.odin b/core/os/errors_windows.odin similarity index 100% rename from core/os/os2/errors_windows.odin rename to core/os/errors_windows.odin diff --git a/core/os/os2/file.odin b/core/os/file.odin similarity index 100% rename from core/os/os2/file.odin rename to core/os/file.odin diff --git a/core/os/os2/file_js.odin b/core/os/file_js.odin similarity index 100% rename from core/os/os2/file_js.odin rename to core/os/file_js.odin diff --git a/core/os/os2/file_linux.odin b/core/os/file_linux.odin similarity index 100% rename from core/os/os2/file_linux.odin rename to core/os/file_linux.odin diff --git a/core/os/os2/file_posix.odin b/core/os/file_posix.odin similarity index 100% rename from core/os/os2/file_posix.odin rename to core/os/file_posix.odin diff --git a/core/os/os2/file_posix_darwin.odin b/core/os/file_posix_darwin.odin similarity index 100% rename from core/os/os2/file_posix_darwin.odin rename to core/os/file_posix_darwin.odin diff --git a/core/os/os2/file_posix_freebsd.odin b/core/os/file_posix_freebsd.odin similarity index 100% rename from core/os/os2/file_posix_freebsd.odin rename to core/os/file_posix_freebsd.odin diff --git a/core/os/os2/file_posix_netbsd.odin b/core/os/file_posix_netbsd.odin similarity index 100% rename from core/os/os2/file_posix_netbsd.odin rename to core/os/file_posix_netbsd.odin diff --git a/core/os/os2/file_posix_other.odin b/core/os/file_posix_other.odin similarity index 100% rename from core/os/os2/file_posix_other.odin rename to core/os/file_posix_other.odin diff --git a/core/os/os2/file_stream.odin b/core/os/file_stream.odin similarity index 100% rename from core/os/os2/file_stream.odin rename to core/os/file_stream.odin diff --git a/core/os/os2/file_util.odin b/core/os/file_util.odin similarity index 100% rename from core/os/os2/file_util.odin rename to core/os/file_util.odin diff --git a/core/os/os2/file_wasi.odin b/core/os/file_wasi.odin similarity index 100% rename from core/os/os2/file_wasi.odin rename to core/os/file_wasi.odin diff --git a/core/os/os2/file_windows.odin b/core/os/file_windows.odin similarity index 100% rename from core/os/os2/file_windows.odin rename to core/os/file_windows.odin diff --git a/core/os/os2/heap.odin b/core/os/heap.odin similarity index 100% rename from core/os/os2/heap.odin rename to core/os/heap.odin diff --git a/core/os/os2/heap_js.odin b/core/os/heap_js.odin similarity index 100% rename from core/os/os2/heap_js.odin rename to core/os/heap_js.odin diff --git a/core/os/os2/heap_linux.odin b/core/os/heap_linux.odin similarity index 100% rename from core/os/os2/heap_linux.odin rename to core/os/heap_linux.odin diff --git a/core/os/os2/heap_posix.odin b/core/os/heap_posix.odin similarity index 100% rename from core/os/os2/heap_posix.odin rename to core/os/heap_posix.odin diff --git a/core/os/os2/heap_wasi.odin b/core/os/heap_wasi.odin similarity index 100% rename from core/os/os2/heap_wasi.odin rename to core/os/heap_wasi.odin diff --git a/core/os/os2/heap_windows.odin b/core/os/heap_windows.odin similarity index 100% rename from core/os/os2/heap_windows.odin rename to core/os/heap_windows.odin diff --git a/core/os/os2/internal_util.odin b/core/os/internal_util.odin similarity index 100% rename from core/os/os2/internal_util.odin rename to core/os/internal_util.odin diff --git a/core/os/dir_unix.odin b/core/os/old/dir_unix.odin similarity index 100% rename from core/os/dir_unix.odin rename to core/os/old/dir_unix.odin diff --git a/core/os/old/dir_windows.odin b/core/os/old/dir_windows.odin new file mode 100644 index 000000000..40f4b9e9b --- /dev/null +++ b/core/os/old/dir_windows.odin @@ -0,0 +1,114 @@ +package os + +import win32 "core:sys/windows" +import "core:strings" +import "base:runtime" + +@(require_results) +read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Error) { + @(require_results) + find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW) -> (fi: File_Info) { + // Ignore "." and ".." + if d.cFileName[0] == '.' && d.cFileName[1] == 0 { + return + } + if d.cFileName[0] == '.' && d.cFileName[1] == '.' && d.cFileName[2] == 0 { + return + } + path := strings.concatenate({base_path, `\`, win32.utf16_to_utf8(d.cFileName[:]) or_else ""}) + fi.fullpath = path + fi.name = basename(path) + fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow) + + if d.dwFileAttributes & win32.FILE_ATTRIBUTE_READONLY != 0 { + fi.mode |= 0o444 + } else { + fi.mode |= 0o666 + } + + is_sym := false + if d.dwFileAttributes & win32.FILE_ATTRIBUTE_REPARSE_Point == 0 { + is_sym = false + } else { + is_sym = d.dwReserved0 == win32.IO_REPARSE_TAG_SYMLINK || d.dwReserved0 == win32.IO_REPARSE_TAG_MOUNT_POINT + } + + if is_sym { + fi.mode |= File_Mode_Sym_Link + } else { + if d.dwFileAttributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 { + fi.mode |= 0o111 | File_Mode_Dir + } + + // fi.mode |= file_type_mode(h); + } + + windows_set_file_info_times(&fi, d) + + fi.is_dir = fi.mode & File_Mode_Dir != 0 + return + } + + if fd == 0 { + return nil, ERROR_INVALID_HANDLE + } + + context.allocator = allocator + + h := win32.HANDLE(fd) + + dir_fi, _ := file_info_from_get_file_information_by_handle("", h) + if !dir_fi.is_dir { + return nil, .Not_Dir + } + + n := n + size := n + if n <= 0 { + n = -1 + size = 100 + } + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) + + wpath := cleanpath_from_handle_u16(fd, context.temp_allocator) or_return + if len(wpath) == 0 { + return + } + + dfi := make([dynamic]File_Info, 0, size) or_return + + wpath_search := make([]u16, len(wpath)+3, context.temp_allocator) or_return + copy(wpath_search, wpath) + wpath_search[len(wpath)+0] = '\\' + wpath_search[len(wpath)+1] = '*' + wpath_search[len(wpath)+2] = 0 + + path := cleanpath_from_buf(wpath) + defer delete(path) + + find_data := &win32.WIN32_FIND_DATAW{} + find_handle := win32.FindFirstFileW(cstring16(raw_data(wpath_search)), find_data) + if find_handle == win32.INVALID_HANDLE_VALUE { + err = get_last_error() + return dfi[:], err + } + defer win32.FindClose(find_handle) + for n != 0 { + fi: File_Info + fi = find_data_to_file_info(path, find_data) + if fi.name != "" { + append(&dfi, fi) + n -= 1 + } + + if !win32.FindNextFileW(find_handle, find_data) { + e := get_last_error() + if e == ERROR_NO_MORE_FILES { + break + } + return dfi[:], e + } + } + + return dfi[:], nil +} diff --git a/core/os/old/env_windows.odin b/core/os/old/env_windows.odin new file mode 100644 index 000000000..ef658b0a1 --- /dev/null +++ b/core/os/old/env_windows.odin @@ -0,0 +1,140 @@ +package os + +import win32 "core:sys/windows" +import "base:runtime" + +// lookup_env gets the value of the environment variable named by the key +// If the variable is found in the environment the value (which can be empty) is returned and the boolean is true +// Otherwise the returned value will be empty and the boolean will be false +// NOTE: the value will be allocated with the supplied allocator +@(require_results) +lookup_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { + if key == "" { + return + } + wkey := win32.utf8_to_wstring(key) + n := win32.GetEnvironmentVariableW(wkey, nil, 0) + if n == 0 && get_last_error() == ERROR_ENVVAR_NOT_FOUND { + return "", false + } + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) + + b, _ := make([dynamic]u16, n, context.temp_allocator) + n = win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b))) + if n == 0 && get_last_error() == ERROR_ENVVAR_NOT_FOUND { + return "", false + } + value, _ = win32.utf16_to_utf8(b[:n], allocator) + found = true + return +} + +// This version of `lookup_env` doesn't allocate and instead requires the user to provide a buffer. +// Note that it is limited to environment names and values of 512 utf-16 values each +// due to the necessary utf-8 <> utf-16 conversion. +@(require_results) +lookup_env_buffer :: proc(buf: []u8, key: string) -> (value: string, err: Error) { + key_buf: [513]u16 + wkey := win32.utf8_to_wstring(key_buf[:], key) + if wkey == nil { + return "", .Buffer_Full + } + + n2 := win32.GetEnvironmentVariableW(wkey, nil, 0) + if n2 == 0 { + return "", .Env_Var_Not_Found + } + + val_buf: [513]u16 + n2 = win32.GetEnvironmentVariableW(wkey, raw_data(val_buf[:]), u32(len(val_buf[:]))) + if n2 == 0 { + return "", .Env_Var_Not_Found + } else if int(n2) > len(buf) { + return "", .Buffer_Full + } + + value = win32.utf16_to_utf8(buf, val_buf[:n2]) + + return value, nil +} +lookup_env :: proc{lookup_env_alloc, lookup_env_buffer} + +// get_env retrieves the value of the environment variable named by the key +// It returns the value, which will be empty if the variable is not present +// To distinguish between an empty value and an unset value, use lookup_env +// NOTE: the value will be allocated with the supplied allocator +@(require_results) +get_env_alloc :: proc(key: string, allocator := context.allocator) -> (value: string) { + value, _ = lookup_env(key, allocator) + return +} + +@(require_results) +get_env_buf :: proc(buf: []u8, key: string) -> (value: string) { + value, _ = lookup_env(buf, key) + return +} +get_env :: proc{get_env_alloc, get_env_buf} + + +// set_env sets the value of the environment variable named by the key +set_env :: proc(key, value: string) -> Error { + k := win32.utf8_to_wstring(key) + v := win32.utf8_to_wstring(value) + + if !win32.SetEnvironmentVariableW(k, v) { + return get_last_error() + } + return nil +} + +// unset_env unsets a single environment variable +unset_env :: proc(key: string) -> Error { + k := win32.utf8_to_wstring(key) + if !win32.SetEnvironmentVariableW(k, nil) { + return get_last_error() + } + return nil +} + +// environ returns a copy of strings representing the environment, in the form "key=value" +// NOTE: the slice of strings and the strings with be allocated using the supplied allocator +@(require_results) +environ :: proc(allocator := context.allocator) -> []string { + envs := ([^]win32.WCHAR)(win32.GetEnvironmentStringsW()) + if envs == nil { + return nil + } + defer win32.FreeEnvironmentStringsW(envs) + + r, err := make([dynamic]string, 0, 50, allocator) + if err != nil { + return nil + } + for from, i := 0, 0; true; i += 1 { + if c := envs[i]; c == 0 { + if i <= from { + break + } + append(&r, win32.utf16_to_utf8(envs[from:i], allocator) or_else "") + from = i + 1 + } + } + + return r[:] +} + + +// clear_env deletes all environment variables +clear_env :: proc() { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() + envs := environ(context.temp_allocator) + for env in envs { + for j in 1.. string where intrinsics.type_is_enum(Platform_Error) { + if e == nil { + return "" + } + + when ODIN_OS == .Darwin { + if s := string(_darwin_string_error(i32(e))); s != "" { + return s + } + } + + when ODIN_OS != .Linux { + @(require_results) + binary_search :: proc "contextless" (array: $A/[]$T, key: T) -> (index: int, found: bool) #no_bounds_check { + n := len(array) + left, right := 0, n + for left < right { + mid := int(uint(left+right) >> 1) + if array[mid] < key { + left = mid+1 + } else { + // equal or greater + right = mid + } + } + return left, left < n && array[left] == key + } + + err := runtime.Type_Info_Enum_Value(e) + + ti := &runtime.type_info_base(type_info_of(Platform_Error)).variant.(runtime.Type_Info_Enum) + if idx, ok := binary_search(ti.values, err); ok { + return ti.names[idx] + } + } else { + @(rodata, static) + pe_strings := [Platform_Error]string{ + .NONE = "", + .EPERM = "Operation not permitted", + .ENOENT = "No such file or directory", + .ESRCH = "No such process", + .EINTR = "Interrupted system call", + .EIO = "Input/output error", + .ENXIO = "No such device or address", + .E2BIG = "Argument list too long", + .ENOEXEC = "Exec format error", + .EBADF = "Bad file descriptor", + .ECHILD = "No child processes", + .EAGAIN = "Resource temporarily unavailable", + .ENOMEM = "Cannot allocate memory", + .EACCES = "Permission denied", + .EFAULT = "Bad address", + .ENOTBLK = "Block device required", + .EBUSY = "Device or resource busy", + .EEXIST = "File exists", + .EXDEV = "Invalid cross-device link", + .ENODEV = "No such device", + .ENOTDIR = "Not a directory", + .EISDIR = "Is a directory", + .EINVAL = "Invalid argument", + .ENFILE = "Too many open files in system", + .EMFILE = "Too many open files", + .ENOTTY = "Inappropriate ioctl for device", + .ETXTBSY = "Text file busy", + .EFBIG = "File too large", + .ENOSPC = "No space left on device", + .ESPIPE = "Illegal seek", + .EROFS = "Read-only file system", + .EMLINK = "Too many links", + .EPIPE = "Broken pipe", + .EDOM = "Numerical argument out of domain", + .ERANGE = "Numerical result out of range", + .EDEADLK = "Resource deadlock avoided", + .ENAMETOOLONG = "File name too long", + .ENOLCK = "No locks available", + .ENOSYS = "Function not implemented", + .ENOTEMPTY = "Directory not empty", + .ELOOP = "Too many levels of symbolic links", + .EUNKNOWN_41 = "Unknown Error (41)", + .ENOMSG = "No message of desired type", + .EIDRM = "Identifier removed", + .ECHRNG = "Channel number out of range", + .EL2NSYNC = "Level 2 not synchronized", + .EL3HLT = "Level 3 halted", + .EL3RST = "Level 3 reset", + .ELNRNG = "Link number out of range", + .EUNATCH = "Protocol driver not attached", + .ENOCSI = "No CSI structure available", + .EL2HLT = "Level 2 halted", + .EBADE = "Invalid exchange", + .EBADR = "Invalid request descriptor", + .EXFULL = "Exchange full", + .ENOANO = "No anode", + .EBADRQC = "Invalid request code", + .EBADSLT = "Invalid slot", + .EUNKNOWN_58 = "Unknown Error (58)", + .EBFONT = "Bad font file format", + .ENOSTR = "Device not a stream", + .ENODATA = "No data available", + .ETIME = "Timer expired", + .ENOSR = "Out of streams resources", + .ENONET = "Machine is not on the network", + .ENOPKG = "Package not installed", + .EREMOTE = "Object is remote", + .ENOLINK = "Link has been severed", + .EADV = "Advertise error", + .ESRMNT = "Srmount error", + .ECOMM = "Communication error on send", + .EPROTO = "Protocol error", + .EMULTIHOP = "Multihop attempted", + .EDOTDOT = "RFS specific error", + .EBADMSG = "Bad message", + .EOVERFLOW = "Value too large for defined data type", + .ENOTUNIQ = "Name not unique on network", + .EBADFD = "File descriptor in bad state", + .EREMCHG = "Remote address changed", + .ELIBACC = "Can not access a needed shared library", + .ELIBBAD = "Accessing a corrupted shared library", + .ELIBSCN = ".lib section in a.out corrupted", + .ELIBMAX = "Attempting to link in too many shared libraries", + .ELIBEXEC = "Cannot exec a shared library directly", + .EILSEQ = "Invalid or incomplete multibyte or wide character", + .ERESTART = "Interrupted system call should be restarted", + .ESTRPIPE = "Streams pipe error", + .EUSERS = "Too many users", + .ENOTSOCK = "Socket operation on non-socket", + .EDESTADDRREQ = "Destination address required", + .EMSGSIZE = "Message too long", + .EPROTOTYPE = "Protocol wrong type for socket", + .ENOPROTOOPT = "Protocol not available", + .EPROTONOSUPPORT = "Protocol not supported", + .ESOCKTNOSUPPORT = "Socket type not supported", + .EOPNOTSUPP = "Operation not supported", + .EPFNOSUPPORT = "Protocol family not supported", + .EAFNOSUPPORT = "Address family not supported by protocol", + .EADDRINUSE = "Address already in use", + .EADDRNOTAVAIL = "Cannot assign requested address", + .ENETDOWN = "Network is down", + .ENETUNREACH = "Network is unreachable", + .ENETRESET = "Network dropped connection on reset", + .ECONNABORTED = "Software caused connection abort", + .ECONNRESET = "Connection reset by peer", + .ENOBUFS = "No buffer space available", + .EISCONN = "Transport endpoint is already connected", + .ENOTCONN = "Transport endpoint is not connected", + .ESHUTDOWN = "Cannot send after transport endpoint shutdown", + .ETOOMANYREFS = "Too many references: cannot splice", + .ETIMEDOUT = "Connection timed out", + .ECONNREFUSED = "Connection refused", + .EHOSTDOWN = "Host is down", + .EHOSTUNREACH = "No route to host", + .EALREADY = "Operation already in progress", + .EINPROGRESS = "Operation now in progress", + .ESTALE = "Stale file handle", + .EUCLEAN = "Structure needs cleaning", + .ENOTNAM = "Not a XENIX named type file", + .ENAVAIL = "No XENIX semaphores available", + .EISNAM = "Is a named type file", + .EREMOTEIO = "Remote I/O error", + .EDQUOT = "Disk quota exceeded", + .ENOMEDIUM = "No medium found", + .EMEDIUMTYPE = "Wrong medium type", + .ECANCELED = "Operation canceled", + .ENOKEY = "Required key not available", + .EKEYEXPIRED = "Key has expired", + .EKEYREVOKED = "Key has been revoked", + .EKEYREJECTED = "Key was rejected by service", + .EOWNERDEAD = "Owner died", + .ENOTRECOVERABLE = "State not recoverable", + .ERFKILL = "Operation not possible due to RF-kill", + .EHWPOISON = "Memory page has hardware error", + } + if Platform_Error.NONE <= e && e <= max(Platform_Error) { + return pe_strings[e] + } + } + return "" +} + +@(private, require_results) +error_to_io_error :: proc(ferr: Error) -> io.Error { + if ferr == nil { + return .None + } + return ferr.(io.Error) or_else .Unknown +} diff --git a/core/os/os.odin b/core/os/old/os.odin similarity index 100% rename from core/os/os.odin rename to core/os/old/os.odin diff --git a/core/os/os_darwin.odin b/core/os/old/os_darwin.odin similarity index 100% rename from core/os/os_darwin.odin rename to core/os/old/os_darwin.odin diff --git a/core/os/os_essence.odin b/core/os/old/os_essence.odin similarity index 100% rename from core/os/os_essence.odin rename to core/os/old/os_essence.odin diff --git a/core/os/os_freebsd.odin b/core/os/old/os_freebsd.odin similarity index 100% rename from core/os/os_freebsd.odin rename to core/os/old/os_freebsd.odin diff --git a/core/os/os_freestanding.odin b/core/os/old/os_freestanding.odin similarity index 100% rename from core/os/os_freestanding.odin rename to core/os/old/os_freestanding.odin diff --git a/core/os/os_haiku.odin b/core/os/old/os_haiku.odin similarity index 100% rename from core/os/os_haiku.odin rename to core/os/old/os_haiku.odin diff --git a/core/os/os_js.odin b/core/os/old/os_js.odin similarity index 100% rename from core/os/os_js.odin rename to core/os/old/os_js.odin diff --git a/core/os/os_linux.odin b/core/os/old/os_linux.odin similarity index 100% rename from core/os/os_linux.odin rename to core/os/old/os_linux.odin diff --git a/core/os/os_netbsd.odin b/core/os/old/os_netbsd.odin similarity index 100% rename from core/os/os_netbsd.odin rename to core/os/old/os_netbsd.odin diff --git a/core/os/os_openbsd.odin b/core/os/old/os_openbsd.odin similarity index 100% rename from core/os/os_openbsd.odin rename to core/os/old/os_openbsd.odin diff --git a/core/os/os_wasi.odin b/core/os/old/os_wasi.odin similarity index 100% rename from core/os/os_wasi.odin rename to core/os/old/os_wasi.odin diff --git a/core/os/os_windows.odin b/core/os/old/os_windows.odin similarity index 100% rename from core/os/os_windows.odin rename to core/os/old/os_windows.odin diff --git a/core/os/old/stat.odin b/core/os/old/stat.odin new file mode 100644 index 000000000..21a4961d1 --- /dev/null +++ b/core/os/old/stat.odin @@ -0,0 +1,33 @@ +package os + +import "core:time" + +File_Info :: struct { + fullpath: string, // allocated + name: string, // uses `fullpath` as underlying data + size: i64, + mode: File_Mode, + is_dir: bool, + creation_time: time.Time, + modification_time: time.Time, + access_time: time.Time, +} + +file_info_slice_delete :: proc(infos: []File_Info, allocator := context.allocator) { + for i := len(infos)-1; i >= 0; i -= 1 { + file_info_delete(infos[i], allocator) + } + delete(infos, allocator) +} + +file_info_delete :: proc(fi: File_Info, allocator := context.allocator) { + delete(fi.fullpath, allocator) +} + +File_Mode :: distinct u32 + +File_Mode_Dir :: File_Mode(1<<16) +File_Mode_Named_Pipe :: File_Mode(1<<17) +File_Mode_Device :: File_Mode(1<<18) +File_Mode_Char_Device :: File_Mode(1<<19) +File_Mode_Sym_Link :: File_Mode(1<<20) diff --git a/core/os/stat_unix.odin b/core/os/old/stat_unix.odin similarity index 100% rename from core/os/stat_unix.odin rename to core/os/old/stat_unix.odin diff --git a/core/os/old/stat_windows.odin b/core/os/old/stat_windows.odin new file mode 100644 index 000000000..662c9f9e6 --- /dev/null +++ b/core/os/old/stat_windows.odin @@ -0,0 +1,303 @@ +package os + +import "core:time" +import "base:runtime" +import win32 "core:sys/windows" + +@(private, require_results) +full_path_from_name :: proc(name: string, allocator := context.allocator) -> (path: string, err: Errno) { + context.allocator = allocator + + name := name + if name == "" { + name = "." + } + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) + p := win32.utf8_to_utf16(name, context.temp_allocator) + buf := make([dynamic]u16, 100) + defer delete(buf) + for { + n := win32.GetFullPathNameW(cstring16(raw_data(p)), u32(len(buf)), cstring16(raw_data(buf)), nil) + if n == 0 { + return "", get_last_error() + } + if n <= u32(len(buf)) { + return win32.utf16_to_utf8(buf[:n], allocator) or_else "", nil + } + resize(&buf, len(buf)*2) + } + + return +} + +@(private, require_results) +_stat :: proc(name: string, create_file_attributes: u32, allocator := context.allocator) -> (fi: File_Info, e: Errno) { + if len(name) == 0 { + return {}, ERROR_PATH_NOT_FOUND + } + + context.allocator = allocator + + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) + + wname := win32.utf8_to_wstring(fix_long_path(name), context.temp_allocator) + fa: win32.WIN32_FILE_ATTRIBUTE_DATA + ok := win32.GetFileAttributesExW(wname, win32.GetFileExInfoStandard, &fa) + if ok && fa.dwFileAttributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 { + // Not a symlink + return file_info_from_win32_file_attribute_data(&fa, name) + } + + err := 0 if ok else win32.GetLastError() + + if err == win32.ERROR_SHARING_VIOLATION { + fd: win32.WIN32_FIND_DATAW + sh := win32.FindFirstFileW(wname, &fd) + if sh == win32.INVALID_HANDLE_VALUE { + e = get_last_error() + return + } + win32.FindClose(sh) + + return file_info_from_win32_find_data(&fd, name) + } + + h := win32.CreateFileW(wname, 0, 0, nil, win32.OPEN_EXISTING, create_file_attributes, nil) + if h == win32.INVALID_HANDLE_VALUE { + e = get_last_error() + return + } + defer win32.CloseHandle(h) + return file_info_from_get_file_information_by_handle(name, h) +} + + +@(require_results) +lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Errno) { + attrs := win32.FILE_FLAG_BACKUP_SEMANTICS + attrs |= win32.FILE_FLAG_OPEN_REPARSE_POINT + return _stat(name, attrs, allocator) +} + +@(require_results) +stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Errno) { + attrs := win32.FILE_FLAG_BACKUP_SEMANTICS + return _stat(name, attrs, allocator) +} + +@(require_results) +fstat :: proc(fd: Handle, allocator := context.allocator) -> (fi: File_Info, err: Errno) { + if fd == 0 { + err = ERROR_INVALID_HANDLE + } + context.allocator = allocator + + path := cleanpath_from_handle(fd) or_return + defer if err != nil { + delete(path) + } + + h := win32.HANDLE(fd) + switch win32.GetFileType(h) { + case win32.FILE_TYPE_PIPE, win32.FILE_TYPE_CHAR: + fi.name = basename(path) + fi.mode |= file_type_mode(h) + err = nil + case: + fi = file_info_from_get_file_information_by_handle(path, h) or_return + } + fi.fullpath = path + return +} + + +@(private, require_results) +cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 { + buf := buf + N := 0 + for c, i in buf { + if c == 0 { break } + N = i+1 + } + buf = buf[:N] + + if len(buf) >= 4 && buf[0] == '\\' && buf[1] == '\\' && buf[2] == '?' && buf[3] == '\\' { + buf = buf[4:] + + /* + NOTE(Jeroen): Properly handle UNC paths. + We need to turn `\\?\UNC\synology.local` into `\\synology.local`. + */ + if len(buf) >= 3 && buf[0] == 'U' && buf[1] == 'N' && buf[2] == 'C' { + buf = buf[2:] + buf[0] = '\\' + } + } + return buf +} + +@(private, require_results) +cleanpath_from_handle :: proc(fd: Handle) -> (s: string, err: Errno) { + runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator) + buf := cleanpath_from_handle_u16(fd, context.temp_allocator) or_return + return win32.utf16_to_utf8(buf, context.allocator) +} +@(private, require_results) +cleanpath_from_handle_u16 :: proc(fd: Handle, allocator: runtime.Allocator) -> ([]u16, Errno) { + if fd == 0 { + return nil, ERROR_INVALID_HANDLE + } + h := win32.HANDLE(fd) + + n := win32.GetFinalPathNameByHandleW(h, nil, 0, 0) + if n == 0 { + return nil, get_last_error() + } + buf := make([]u16, max(n, win32.DWORD(260))+1, allocator) + buf_len := win32.GetFinalPathNameByHandleW(h, cstring16(raw_data(buf)), n, 0) + return buf[:buf_len], nil +} +@(private, require_results) +cleanpath_from_buf :: proc(buf: []u16) -> string { + buf := buf + buf = cleanpath_strip_prefix(buf) + return win32.utf16_to_utf8(buf, context.allocator) or_else "" +} + +@(private, require_results) +basename :: proc(name: string) -> (base: string) { + name := name + if len(name) > 3 && name[:3] == `\\?` { + name = name[3:] + } + + if len(name) == 2 && name[1] == ':' { + return "." + } else if len(name) > 2 && name[1] == ':' { + name = name[2:] + } + i := len(name)-1 + + for ; i > 0 && (name[i] == '/' || name[i] == '\\'); i -= 1 { + name = name[:i] + } + for i -= 1; i >= 0; i -= 1 { + if name[i] == '/' || name[i] == '\\' { + name = name[i+1:] + break + } + } + return name +} + +@(private, require_results) +file_type_mode :: proc(h: win32.HANDLE) -> File_Mode { + switch win32.GetFileType(h) { + case win32.FILE_TYPE_PIPE: + return File_Mode_Named_Pipe + case win32.FILE_TYPE_CHAR: + return File_Mode_Device | File_Mode_Char_Device + } + return 0 +} + + +@(private, require_results) +file_mode_from_file_attributes :: proc(FileAttributes: win32.DWORD, h: win32.HANDLE, ReparseTag: win32.DWORD) -> (mode: File_Mode) { + if FileAttributes & win32.FILE_ATTRIBUTE_READONLY != 0 { + mode |= 0o444 + } else { + mode |= 0o666 + } + + is_sym := false + if FileAttributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 { + is_sym = false + } else { + is_sym = ReparseTag == win32.IO_REPARSE_TAG_SYMLINK || ReparseTag == win32.IO_REPARSE_TAG_MOUNT_POINT + } + + if is_sym { + mode |= File_Mode_Sym_Link + } else { + if FileAttributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 { + mode |= 0o111 | File_Mode_Dir + } + + if h != nil { + mode |= file_type_mode(h) + } + } + + return +} + +@(private) +windows_set_file_info_times :: proc(fi: ^File_Info, d: ^$T) { + fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime)) + fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime)) + fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime)) +} + +@(private, require_results) +file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string) -> (fi: File_Info, e: Errno) { + fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow) + + fi.mode |= file_mode_from_file_attributes(d.dwFileAttributes, nil, 0) + fi.is_dir = fi.mode & File_Mode_Dir != 0 + + windows_set_file_info_times(&fi, d) + + fi.fullpath, e = full_path_from_name(name) + fi.name = basename(fi.fullpath) + + return +} + +@(private, require_results) +file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string) -> (fi: File_Info, e: Errno) { + fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow) + + fi.mode |= file_mode_from_file_attributes(d.dwFileAttributes, nil, 0) + fi.is_dir = fi.mode & File_Mode_Dir != 0 + + windows_set_file_info_times(&fi, d) + + fi.fullpath, e = full_path_from_name(name) + fi.name = basename(fi.fullpath) + + return +} + +@(private, require_results) +file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HANDLE) -> (File_Info, Errno) { + d: win32.BY_HANDLE_FILE_INFORMATION + if !win32.GetFileInformationByHandle(h, &d) { + err := get_last_error() + return {}, err + + } + + ti: win32.FILE_ATTRIBUTE_TAG_INFO + if !win32.GetFileInformationByHandleEx(h, .FileAttributeTagInfo, &ti, size_of(ti)) { + err := get_last_error() + if err != ERROR_INVALID_PARAMETER { + return {}, err + } + // Indicate this is a symlink on FAT file systems + ti.ReparseTag = 0 + } + + fi: File_Info + + fi.fullpath = path + fi.name = basename(path) + fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow) + + fi.mode |= file_mode_from_file_attributes(ti.FileAttributes, h, ti.ReparseTag) + fi.is_dir = fi.mode & File_Mode_Dir != 0 + + windows_set_file_info_times(&fi, &d) + + return fi, nil +} diff --git a/core/os/stream.odin b/core/os/old/stream.odin similarity index 100% rename from core/os/stream.odin rename to core/os/old/stream.odin diff --git a/core/os/os2/dir_windows.odin b/core/os/os2/dir_windows.odin deleted file mode 100644 index a4dadca75..000000000 --- a/core/os/os2/dir_windows.odin +++ /dev/null @@ -1,144 +0,0 @@ -#+private -package os2 - -import "base:runtime" -import "core:time" -import win32 "core:sys/windows" - -@(private="file") -find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) { - // Ignore "." and ".." - if d.cFileName[0] == '.' && d.cFileName[1] == 0 { - return - } - if d.cFileName[0] == '.' && d.cFileName[1] == '.' && d.cFileName[2] == 0 { - return - } - - temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) - path := concatenate({base_path, `\`, win32_wstring_to_utf8(cstring16(raw_data(d.cFileName[:])), temp_allocator) or_else ""}, allocator) or_return - - handle := win32.HANDLE(_open_internal(path, {.Read}, Permissions_Read_Write_All) or_else 0) - defer win32.CloseHandle(handle) - - fi.fullpath = path - fi.name = basename(path) - fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow) - - fi.type, fi.mode = _file_type_mode_from_file_attributes(d.dwFileAttributes, handle, d.dwReserved0) - - fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime)) - fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime)) - fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime)) - - if file_id_info: win32.FILE_ID_INFO; handle != nil && win32.GetFileInformationByHandleEx(handle, .FileIdInfo, &file_id_info, size_of(file_id_info)) { - #assert(size_of(fi.inode) == size_of(file_id_info.FileId)) - #assert(size_of(fi.inode) == 16) - runtime.mem_copy_non_overlapping(&fi.inode, &file_id_info.FileId, 16) - } - - return -} - -Read_Directory_Iterator_Impl :: struct { - find_data: win32.WIN32_FIND_DATAW, - find_handle: win32.HANDLE, - path: string, - prev_fi: File_Info, - no_more_files: bool, -} - - -@(require_results) -_read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) { - for !it.impl.no_more_files { - err: Error - file_info_delete(it.impl.prev_fi, file_allocator()) - it.impl.prev_fi = {} - - fi, err = find_data_to_file_info(it.impl.path, &it.impl.find_data, file_allocator()) - if err != nil { - read_directory_iterator_set_error(it, it.impl.path, err) - return - } - - if fi.name != "" { - it.impl.prev_fi = fi - ok = true - index = it.index - it.index += 1 - } - - if !win32.FindNextFileW(it.impl.find_handle, &it.impl.find_data) { - e := _get_platform_error() - if pe, _ := is_platform_error(e); pe != i32(win32.ERROR_NO_MORE_FILES) { - read_directory_iterator_set_error(it, it.impl.path, e) - } - it.impl.no_more_files = true - } - if ok { - return - } - } - return -} - -_read_directory_iterator_init :: proc(it: ^Read_Directory_Iterator, f: ^File) { - it.impl.no_more_files = false - - if f == nil || f.impl == nil { - read_directory_iterator_set_error(it, "", .Invalid_File) - return - } - - it.f = f - impl := (^File_Impl)(f.impl) - - // NOTE: Allow calling `init` to target a new directory with the same iterator - reset idx. - if it.impl.find_handle != nil { - win32.FindClose(it.impl.find_handle) - } - if it.impl.path != "" { - delete(it.impl.path, file_allocator()) - } - - if !is_directory(impl.name) { - read_directory_iterator_set_error(it, impl.name, .Invalid_Dir) - return - } - - wpath := string16(impl.wname) - temp_allocator := TEMP_ALLOCATOR_GUARD({}) - - wpath_search := make([]u16, len(wpath)+3, temp_allocator) - copy(wpath_search, wpath) - wpath_search[len(wpath)+0] = '\\' - wpath_search[len(wpath)+1] = '*' - wpath_search[len(wpath)+2] = 0 - - it.impl.find_handle = win32.FindFirstFileW(cstring16(raw_data(wpath_search)), &it.impl.find_data) - if it.impl.find_handle == win32.INVALID_HANDLE_VALUE { - read_directory_iterator_set_error(it, impl.name, _get_platform_error()) - return - } - defer if it.err.err != nil { - win32.FindClose(it.impl.find_handle) - } - - err: Error - it.impl.path, err = _cleanpath_from_buf(wpath, file_allocator()) - if err != nil { - read_directory_iterator_set_error(it, impl.name, err) - } - - return -} - -_read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) { - if it.f == nil { - return - } - file_info_delete(it.impl.prev_fi, file_allocator()) - delete(it.impl.path, file_allocator()) - win32.FindClose(it.impl.find_handle) -} diff --git a/core/os/os2/env_windows.odin b/core/os/os2/env_windows.odin deleted file mode 100644 index d389f8860..000000000 --- a/core/os/os2/env_windows.odin +++ /dev/null @@ -1,142 +0,0 @@ -#+private -package os2 - -import win32 "core:sys/windows" -import "base:runtime" - -_lookup_env_alloc :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) { - if key == "" { - return - } - temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) - wkey, _ := win32_utf8_to_wstring(key, temp_allocator) - - n := win32.GetEnvironmentVariableW(wkey, nil, 0) - if n == 0 { - err := win32.GetLastError() - if err == win32.ERROR_ENVVAR_NOT_FOUND { - return "", false - } - return "", true - } - - b := make([]u16, n+1, temp_allocator) - - n = win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b))) - if n == 0 { - err := win32.GetLastError() - if err == win32.ERROR_ENVVAR_NOT_FOUND { - return "", false - } - return "", false - } - - value = win32_utf16_to_utf8(string16(b[:n]), allocator) or_else "" - found = true - return -} - -// This version of `lookup_env` doesn't allocate and instead requires the user to provide a buffer. -// Note that it is limited to environment names and values of 512 utf-16 values each -// due to the necessary utf-8 <> utf-16 conversion. -@(require_results) -_lookup_env_buf :: proc(buf: []u8, key: string) -> (value: string, err: Error) { - key_buf: [513]u16 - wkey := win32.utf8_to_wstring(key_buf[:], key) - if wkey == nil { - return "", .Buffer_Full - } - - n2 := win32.GetEnvironmentVariableW(wkey, nil, 0) - if n2 == 0 { - return "", .Env_Var_Not_Found - } - - val_buf: [513]u16 - n2 = win32.GetEnvironmentVariableW(wkey, raw_data(val_buf[:]), u32(len(val_buf[:]))) - if n2 == 0 { - return "", .Env_Var_Not_Found - } else if int(n2) > len(buf) { - return "", .Buffer_Full - } - - value = win32.utf16_to_utf8(buf, val_buf[:n2]) - - return value, nil -} -_lookup_env :: proc{_lookup_env_alloc, _lookup_env_buf} - -_set_env :: proc(key, value: string) -> Error { - temp_allocator := TEMP_ALLOCATOR_GUARD({}) - k := win32_utf8_to_wstring(key, temp_allocator) or_return - v := win32_utf8_to_wstring(value, temp_allocator) or_return - - if !win32.SetEnvironmentVariableW(k, v) { - return _get_platform_error() - } - return nil -} - -_unset_env :: proc(key: string) -> bool { - temp_allocator := TEMP_ALLOCATOR_GUARD({}) - k, _ := win32_utf8_to_wstring(key, temp_allocator) - return bool(win32.SetEnvironmentVariableW(k, nil)) -} - -_clear_env :: proc() { - temp_allocator := TEMP_ALLOCATOR_GUARD({}) - envs, _ := environ(temp_allocator) - for env in envs { - for j in 1.. io.Error { - if ferr == nil { - return .None - } - return ferr.(io.Error) or_else .Unknown -} diff --git a/core/os/os2/stat.odin b/core/os/os2/stat.odin deleted file mode 100644 index 0a9ac4e57..000000000 --- a/core/os/os2/stat.odin +++ /dev/null @@ -1,117 +0,0 @@ -package os2 - -import "base:runtime" -import "core:strings" -import "core:time" - -Fstat_Callback :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) - -/* - `File_Info` describes a file and is returned from `stat`, `fstat`, and `lstat`. -*/ -File_Info :: struct { - fullpath: string, // fullpath of the file - name: string, // base name of the file - - inode: u128, // might be zero if cannot be determined - size: i64 `fmt:"M"`, // length in bytes for regular files; system-dependent for other file types - mode: Permissions, // file permission flags - type: File_Type, - - creation_time: time.Time, - modification_time: time.Time, - access_time: time.Time, -} - -@(require_results) -file_info_clone :: proc(fi: File_Info, allocator: runtime.Allocator) -> (cloned: File_Info, err: runtime.Allocator_Error) { - cloned = fi - cloned.fullpath = strings.clone(fi.fullpath, allocator) or_return - _, cloned.name = split_path(cloned.fullpath) - return -} - -file_info_slice_delete :: proc(infos: []File_Info, allocator: runtime.Allocator) { - #reverse for info in infos { - file_info_delete(info, allocator) - } - delete(infos, allocator) -} - -file_info_delete :: proc(fi: File_Info, allocator: runtime.Allocator) { - delete(fi.fullpath, allocator) -} - -@(require_results) -fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) { - if f == nil { - return {}, nil - } else if f.stream.procedure != nil { - fi: File_Info - data := ([^]byte)(&fi)[:size_of(fi)] - _, err := f.stream.procedure(f, .Fstat, data, 0, nil, allocator) - return fi, err - } - return {}, .Invalid_Callback -} - -/* - `stat` returns a `File_Info` describing the named file from the file system. - The resulting `File_Info` must be deleted with `file_info_delete`. -*/ -@(require_results) -stat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) { - return _stat(name, allocator) -} - -lstat :: stat_do_not_follow_links - -/* - Returns a `File_Info` describing the named file from the file system. - If the file is a symbolic link, the `File_Info` returns describes the symbolic link, - rather than following the link. - The resulting `File_Info` must be deleted with `file_info_delete`. -*/ -@(require_results) -stat_do_not_follow_links :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) { - return _lstat(name, allocator) -} - - -/* - Returns true if two `File_Info`s are equivalent. -*/ -@(require_results) -same_file :: proc(fi1, fi2: File_Info) -> bool { - return _same_file(fi1, fi2) -} - - -last_write_time :: modification_time -last_write_time_by_name :: modification_time_by_path - -/* - Returns the modification time of the file `f`. - The resolution of the timestamp is system-dependent. -*/ -@(require_results) -modification_time :: proc(f: ^File) -> (time.Time, Error) { - temp_allocator := TEMP_ALLOCATOR_GUARD({}) - fi, err := fstat(f, temp_allocator) - return fi.modification_time, err -} - -/* - Returns the modification time of the named file `path`. - The resolution of the timestamp is system-dependent. -*/ -@(require_results) -modification_time_by_path :: proc(path: string) -> (time.Time, Error) { - temp_allocator := TEMP_ALLOCATOR_GUARD({}) - fi, err := stat(path, temp_allocator) - return fi.modification_time, err -} - -is_reserved_name :: proc(path: string) -> bool { - return _is_reserved_name(path) -} \ No newline at end of file diff --git a/core/os/os2/stat_windows.odin b/core/os/os2/stat_windows.odin deleted file mode 100644 index 651029ac3..000000000 --- a/core/os/os2/stat_windows.odin +++ /dev/null @@ -1,393 +0,0 @@ -#+private -package os2 - -import "base:runtime" -import "core:time" -import "core:strings" -import win32 "core:sys/windows" - -_fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) { - if f == nil || (^File_Impl)(f.impl).fd == nil { - return - } - - path := _cleanpath_from_handle(f, allocator) or_return - - h := _handle(f) - switch win32.GetFileType(h) { - case win32.FILE_TYPE_PIPE, win32.FILE_TYPE_CHAR: - fi = File_Info { - fullpath = path, - name = basename(path), - type = file_type(h), - } - return - } - - return _file_info_from_get_file_information_by_handle(path, h, allocator) -} - -_stat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) { - return internal_stat(name, win32.FILE_FLAG_BACKUP_SEMANTICS, allocator) -} - -_lstat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) { - return internal_stat(name, win32.FILE_FLAG_BACKUP_SEMANTICS|win32.FILE_FLAG_OPEN_REPARSE_POINT, allocator) -} - -_same_file :: proc(fi1, fi2: File_Info) -> bool { - return fi1.fullpath == fi2.fullpath -} - -full_path_from_name :: proc(name: string, allocator: runtime.Allocator) -> (path: string, err: Error) { - name := name - if name == "" { - name = "." - } - - temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) - - p := win32_utf8_to_utf16(name, temp_allocator) or_return - - n := win32.GetFullPathNameW(cstring16(raw_data(p)), 0, nil, nil) - if n == 0 { - return "", _get_platform_error() - } - buf := make([]u16, n+1, temp_allocator) - n = win32.GetFullPathNameW(cstring16(raw_data(p)), u32(len(buf)), cstring16(raw_data(buf)), nil) - if n == 0 { - return "", _get_platform_error() - } - return win32_utf16_to_utf8(buf[:n], allocator) -} - -internal_stat :: proc(name: string, create_file_attributes: u32, allocator: runtime.Allocator) -> (fi: File_Info, e: Error) { - if len(name) == 0 { - return {}, .Not_Exist - } - temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) - - wname := _fix_long_path(name, temp_allocator) or_return - fa: win32.WIN32_FILE_ATTRIBUTE_DATA - ok := win32.GetFileAttributesExW(wname, win32.GetFileExInfoStandard, &fa) - if ok && fa.dwFileAttributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 { - // Not a symlink - fi = _file_info_from_win32_file_attribute_data(&fa, name, allocator) or_return - if fi.type == .Undetermined { - fi.type = _file_type_from_create_file(wname, create_file_attributes) - } - return - } - - err := 0 if ok else win32.GetLastError() - - if err == win32.ERROR_SHARING_VIOLATION { - fd: win32.WIN32_FIND_DATAW - sh := win32.FindFirstFileW(wname, &fd) - if sh == win32.INVALID_HANDLE_VALUE { - e = _get_platform_error() - return - } - win32.FindClose(sh) - - fi = _file_info_from_win32_find_data(&fd, name, allocator) or_return - if fi.type == .Undetermined { - fi.type = _file_type_from_create_file(wname, create_file_attributes) - } - return - } - - h := win32.CreateFileW(wname, 0, 0, nil, win32.OPEN_EXISTING, create_file_attributes, nil) - if h == win32.INVALID_HANDLE_VALUE { - e = _get_platform_error() - return - } - defer win32.CloseHandle(h) - return _file_info_from_get_file_information_by_handle(name, h, allocator) -} - -_cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 { - buf := buf - N := 0 - for c, i in buf { - if c == 0 { break } - N = i+1 - } - buf = buf[:N] - - if len(buf) >= 4 { - if buf[0] == '\\' && - buf[1] == '\\' && - buf[2] == '?' && - buf[3] == '\\' { - buf = buf[4:] - } - } - return buf -} - -_cleanpath_from_handle :: proc(f: ^File, allocator: runtime.Allocator) -> (string, Error) { - if f == nil { - return "", nil - } - h := _handle(f) - - n := win32.GetFinalPathNameByHandleW(h, nil, 0, 0) - if n == 0 { - return "", _get_platform_error() - } - - temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) - - buf := make([]u16, max(n, 260)+1, temp_allocator) - n = win32.GetFinalPathNameByHandleW(h, cstring16(raw_data(buf)), u32(len(buf)), 0) - return _cleanpath_from_buf(string16(buf[:n]), allocator) -} - -_cleanpath_from_handle_u16 :: proc(f: ^File) -> ([]u16, Error) { - if f == nil { - return nil, nil - } - h := _handle(f) - - n := win32.GetFinalPathNameByHandleW(h, nil, 0, 0) - if n == 0 { - return nil, _get_platform_error() - } - - temp_allocator := TEMP_ALLOCATOR_GUARD({}) - - buf := make([]u16, max(n, 260)+1, temp_allocator) - n = win32.GetFinalPathNameByHandleW(h, cstring16(raw_data(buf)), u32(len(buf)), 0) - return _cleanpath_strip_prefix(buf[:n]), nil -} - -_cleanpath_from_buf :: proc(buf: string16, allocator: runtime.Allocator) -> (string, runtime.Allocator_Error) { - buf := transmute([]u16)buf - buf = _cleanpath_strip_prefix(buf) - return win32_utf16_to_utf8(buf, allocator) -} - -basename :: proc(name: string) -> (base: string) { - name := name - if len(name) > 3 && name[:3] == `\\?` { - name = name[3:] - } - - if len(name) == 2 && name[1] == ':' { - return "." - } else if len(name) > 2 && name[1] == ':' { - name = name[2:] - } - i := len(name)-1 - - for ; i > 0 && (name[i] == '/' || name[i] == '\\'); i -= 1 { - name = name[:i] - } - for i -= 1; i >= 0; i -= 1 { - if name[i] == '/' || name[i] == '\\' { - name = name[i+1:] - break - } - } - return name -} - -file_type :: proc(h: win32.HANDLE) -> File_Type { - switch win32.GetFileType(h) { - case win32.FILE_TYPE_PIPE: return .Named_Pipe - case win32.FILE_TYPE_CHAR: return .Character_Device - case win32.FILE_TYPE_DISK: return .Regular - } - return .Undetermined -} - -_file_type_from_create_file :: proc(wname: win32.wstring, create_file_attributes: u32) -> File_Type { - h := win32.CreateFileW(wname, 0, 0, nil, win32.OPEN_EXISTING, create_file_attributes, nil) - if h == win32.INVALID_HANDLE_VALUE { - return .Undetermined - } - defer win32.CloseHandle(h) - return file_type(h) -} - -_file_type_mode_from_file_attributes :: proc(file_attributes: win32.DWORD, h: win32.HANDLE, ReparseTag: win32.DWORD) -> (type: File_Type, mode: Permissions) { - if file_attributes & win32.FILE_ATTRIBUTE_READONLY != 0 { - mode += Permissions_Write_All - } else { - mode += Permissions_Read_Write_All - } - - is_sym := false - if file_attributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 { - is_sym = false - } else { - is_sym = ReparseTag == win32.IO_REPARSE_TAG_SYMLINK || ReparseTag == win32.IO_REPARSE_TAG_MOUNT_POINT - } - - if is_sym { - type = .Symlink - } else if file_attributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 { - type = .Directory - mode += Permissions_Execute_All - } else if h != nil { - type = file_type(h) - } - return -} - -// a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC) -time_as_filetime :: #force_inline proc(t: time.Time) -> (ft: win32.LARGE_INTEGER) { - win := u64(t._nsec / 100) + 116444736000000000 - return win32.LARGE_INTEGER(win) -} - -filetime_as_time_li :: #force_inline proc(ft: win32.LARGE_INTEGER) -> (t: time.Time) { - return {_nsec=(i64(ft) - 116444736000000000) * 100} -} - -filetime_as_time_ft :: #force_inline proc(ft: win32.FILETIME) -> (t: time.Time) { - return filetime_as_time_li(win32.LARGE_INTEGER(ft.dwLowDateTime) + win32.LARGE_INTEGER(ft.dwHighDateTime) << 32) -} - -filetime_as_time :: proc{filetime_as_time_ft, filetime_as_time_li} - -_file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string, allocator: runtime.Allocator) -> (fi: File_Info, e: Error) { - fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow) - type, mode := _file_type_mode_from_file_attributes(d.dwFileAttributes, nil, 0) - fi.type = type - fi.mode |= mode - fi.creation_time = filetime_as_time(d.ftCreationTime) - fi.modification_time = filetime_as_time(d.ftLastWriteTime) - fi.access_time = filetime_as_time(d.ftLastAccessTime) - fi.fullpath, e = full_path_from_name(name, allocator) - fi.name = basename(fi.fullpath) - return -} - -_file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string, allocator: runtime.Allocator) -> (fi: File_Info, e: Error) { - fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow) - type, mode := _file_type_mode_from_file_attributes(d.dwFileAttributes, nil, 0) - fi.type = type - fi.mode |= mode - fi.creation_time = filetime_as_time(d.ftCreationTime) - fi.modification_time = filetime_as_time(d.ftLastWriteTime) - fi.access_time = filetime_as_time(d.ftLastAccessTime) - fi.fullpath, e = full_path_from_name(name, allocator) - fi.name = basename(fi.fullpath) - return -} - -_file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HANDLE, allocator: runtime.Allocator) -> (File_Info, Error) { - d: win32.BY_HANDLE_FILE_INFORMATION - if !win32.GetFileInformationByHandle(h, &d) { - return {}, _get_platform_error() - - } - - ti: win32.FILE_ATTRIBUTE_TAG_INFO - if !win32.GetFileInformationByHandleEx(h, .FileAttributeTagInfo, &ti, size_of(ti)) { - err := _get_platform_error() - if perr, ok := is_platform_error(err); ok && perr != i32(win32.ERROR_INVALID_PARAMETER) { - return {}, err - } - // Indicate this is a symlink on FAT file systems - ti.ReparseTag = 0 - } - fi: File_Info - fi.fullpath = path - fi.name = basename(path) - fi.inode = u128(u64(d.nFileIndexHigh)<<32 + u64(d.nFileIndexLow)) - fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow) - type, mode := _file_type_mode_from_file_attributes(d.dwFileAttributes, h, 0) - fi.type = type - fi.mode |= mode - fi.creation_time = filetime_as_time(d.ftCreationTime) - fi.modification_time = filetime_as_time(d.ftLastWriteTime) - fi.access_time = filetime_as_time(d.ftLastAccessTime) - return fi, nil -} - -reserved_names := [?]string{ - "CON", "PRN", "AUX", "NUL", - "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", - "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", -} - -_is_reserved_name :: proc(path: string) -> bool { - if len(path) == 0 { - return false - } - for reserved in reserved_names { - if strings.equal_fold(path, reserved) { - return true - } - } - return false -} - -_volume_name_len :: proc(path: string) -> (length: int) { - if len(path) < 2 { - return 0 - } - - if path[1] == ':' { - switch path[0] { - case 'a'..='z', 'A'..='Z': - return 2 - } - } - - /* - See: URL: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx - Further allowed paths can be of the form of: - - \\server\share or \\server\share\more\path - - \\?\C:\... - - \\.\PhysicalDriveX - */ - // Any remaining kind of path has to start with two slashes. - if !_is_path_separator(path[0]) || !_is_path_separator(path[1]) { - return 0 - } - - // Device path. The volume name is the whole string - if len(path) >= 5 && path[2] == '.' && _is_path_separator(path[3]) { - return len(path) - } - - // We're a UNC share `\\host\share`, file namespace `\\?\C:` or UNC in file namespace `\\?\\host\share` - prefix := 2 - - // File namespace. - if len(path) >= 5 && path[2] == '?' && _is_path_separator(path[3]) { - if _is_path_separator(path[4]) { - // `\\?\\` UNC path in file namespace - prefix = 5 - } - - if len(path) >= 6 && path[5] == ':' { - switch path[4] { - case 'a'..='z', 'A'..='Z': - return 6 - case: - return 0 - } - } - } - - // UNC path, minimum version of the volume is `\\h\s` for host, share. - // Can also contain an IP address in the host position. - slash_count := 0 - for i in prefix.. 0 { - slash_count += 1 - - if slash_count == 2 { - return i - } - } - } - - return len(path) -} \ No newline at end of file diff --git a/core/os/os2/path.odin b/core/os/path.odin similarity index 100% rename from core/os/os2/path.odin rename to core/os/path.odin diff --git a/core/os/os2/path_darwin.odin b/core/os/path_darwin.odin similarity index 100% rename from core/os/os2/path_darwin.odin rename to core/os/path_darwin.odin diff --git a/core/os/os2/path_freebsd.odin b/core/os/path_freebsd.odin similarity index 100% rename from core/os/os2/path_freebsd.odin rename to core/os/path_freebsd.odin diff --git a/core/os/os2/path_js.odin b/core/os/path_js.odin similarity index 100% rename from core/os/os2/path_js.odin rename to core/os/path_js.odin diff --git a/core/os/os2/path_linux.odin b/core/os/path_linux.odin similarity index 100% rename from core/os/os2/path_linux.odin rename to core/os/path_linux.odin diff --git a/core/os/os2/path_netbsd.odin b/core/os/path_netbsd.odin similarity index 100% rename from core/os/os2/path_netbsd.odin rename to core/os/path_netbsd.odin diff --git a/core/os/os2/path_openbsd.odin b/core/os/path_openbsd.odin similarity index 100% rename from core/os/os2/path_openbsd.odin rename to core/os/path_openbsd.odin diff --git a/core/os/os2/path_posix.odin b/core/os/path_posix.odin similarity index 100% rename from core/os/os2/path_posix.odin rename to core/os/path_posix.odin diff --git a/core/os/os2/path_posixfs.odin b/core/os/path_posixfs.odin similarity index 100% rename from core/os/os2/path_posixfs.odin rename to core/os/path_posixfs.odin diff --git a/core/os/os2/path_wasi.odin b/core/os/path_wasi.odin similarity index 100% rename from core/os/os2/path_wasi.odin rename to core/os/path_wasi.odin diff --git a/core/os/os2/path_windows.odin b/core/os/path_windows.odin similarity index 100% rename from core/os/os2/path_windows.odin rename to core/os/path_windows.odin diff --git a/core/os/os2/pipe.odin b/core/os/pipe.odin similarity index 100% rename from core/os/os2/pipe.odin rename to core/os/pipe.odin diff --git a/core/os/os2/pipe_js.odin b/core/os/pipe_js.odin similarity index 100% rename from core/os/os2/pipe_js.odin rename to core/os/pipe_js.odin diff --git a/core/os/os2/pipe_linux.odin b/core/os/pipe_linux.odin similarity index 100% rename from core/os/os2/pipe_linux.odin rename to core/os/pipe_linux.odin diff --git a/core/os/os2/pipe_posix.odin b/core/os/pipe_posix.odin similarity index 100% rename from core/os/os2/pipe_posix.odin rename to core/os/pipe_posix.odin diff --git a/core/os/os2/pipe_wasi.odin b/core/os/pipe_wasi.odin similarity index 100% rename from core/os/os2/pipe_wasi.odin rename to core/os/pipe_wasi.odin diff --git a/core/os/os2/pipe_windows.odin b/core/os/pipe_windows.odin similarity index 100% rename from core/os/os2/pipe_windows.odin rename to core/os/pipe_windows.odin diff --git a/core/os/os2/process.odin b/core/os/process.odin similarity index 100% rename from core/os/os2/process.odin rename to core/os/process.odin diff --git a/core/os/os2/process_freebsd.odin b/core/os/process_freebsd.odin similarity index 100% rename from core/os/os2/process_freebsd.odin rename to core/os/process_freebsd.odin diff --git a/core/os/os2/process_js.odin b/core/os/process_js.odin similarity index 100% rename from core/os/os2/process_js.odin rename to core/os/process_js.odin diff --git a/core/os/os2/process_linux.odin b/core/os/process_linux.odin similarity index 100% rename from core/os/os2/process_linux.odin rename to core/os/process_linux.odin diff --git a/core/os/os2/process_netbsd.odin b/core/os/process_netbsd.odin similarity index 100% rename from core/os/os2/process_netbsd.odin rename to core/os/process_netbsd.odin diff --git a/core/os/os2/process_openbsd.odin b/core/os/process_openbsd.odin similarity index 100% rename from core/os/os2/process_openbsd.odin rename to core/os/process_openbsd.odin diff --git a/core/os/os2/process_posix.odin b/core/os/process_posix.odin similarity index 100% rename from core/os/os2/process_posix.odin rename to core/os/process_posix.odin diff --git a/core/os/os2/process_posix_darwin.odin b/core/os/process_posix_darwin.odin similarity index 100% rename from core/os/os2/process_posix_darwin.odin rename to core/os/process_posix_darwin.odin diff --git a/core/os/os2/process_posix_other.odin b/core/os/process_posix_other.odin similarity index 100% rename from core/os/os2/process_posix_other.odin rename to core/os/process_posix_other.odin diff --git a/core/os/os2/process_wasi.odin b/core/os/process_wasi.odin similarity index 100% rename from core/os/os2/process_wasi.odin rename to core/os/process_wasi.odin diff --git a/core/os/os2/process_windows.odin b/core/os/process_windows.odin similarity index 100% rename from core/os/os2/process_windows.odin rename to core/os/process_windows.odin diff --git a/core/os/stat.odin b/core/os/stat.odin index 21a4961d1..0a9ac4e57 100644 --- a/core/os/stat.odin +++ b/core/os/stat.odin @@ -1,33 +1,117 @@ -package os +package os2 +import "base:runtime" +import "core:strings" import "core:time" +Fstat_Callback :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) + +/* + `File_Info` describes a file and is returned from `stat`, `fstat`, and `lstat`. +*/ File_Info :: struct { - fullpath: string, // allocated - name: string, // uses `fullpath` as underlying data - size: i64, - mode: File_Mode, - is_dir: bool, + fullpath: string, // fullpath of the file + name: string, // base name of the file + + inode: u128, // might be zero if cannot be determined + size: i64 `fmt:"M"`, // length in bytes for regular files; system-dependent for other file types + mode: Permissions, // file permission flags + type: File_Type, + creation_time: time.Time, modification_time: time.Time, access_time: time.Time, } -file_info_slice_delete :: proc(infos: []File_Info, allocator := context.allocator) { - for i := len(infos)-1; i >= 0; i -= 1 { - file_info_delete(infos[i], allocator) +@(require_results) +file_info_clone :: proc(fi: File_Info, allocator: runtime.Allocator) -> (cloned: File_Info, err: runtime.Allocator_Error) { + cloned = fi + cloned.fullpath = strings.clone(fi.fullpath, allocator) or_return + _, cloned.name = split_path(cloned.fullpath) + return +} + +file_info_slice_delete :: proc(infos: []File_Info, allocator: runtime.Allocator) { + #reverse for info in infos { + file_info_delete(info, allocator) } delete(infos, allocator) } -file_info_delete :: proc(fi: File_Info, allocator := context.allocator) { +file_info_delete :: proc(fi: File_Info, allocator: runtime.Allocator) { delete(fi.fullpath, allocator) } -File_Mode :: distinct u32 +@(require_results) +fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) { + if f == nil { + return {}, nil + } else if f.stream.procedure != nil { + fi: File_Info + data := ([^]byte)(&fi)[:size_of(fi)] + _, err := f.stream.procedure(f, .Fstat, data, 0, nil, allocator) + return fi, err + } + return {}, .Invalid_Callback +} -File_Mode_Dir :: File_Mode(1<<16) -File_Mode_Named_Pipe :: File_Mode(1<<17) -File_Mode_Device :: File_Mode(1<<18) -File_Mode_Char_Device :: File_Mode(1<<19) -File_Mode_Sym_Link :: File_Mode(1<<20) +/* + `stat` returns a `File_Info` describing the named file from the file system. + The resulting `File_Info` must be deleted with `file_info_delete`. +*/ +@(require_results) +stat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) { + return _stat(name, allocator) +} + +lstat :: stat_do_not_follow_links + +/* + Returns a `File_Info` describing the named file from the file system. + If the file is a symbolic link, the `File_Info` returns describes the symbolic link, + rather than following the link. + The resulting `File_Info` must be deleted with `file_info_delete`. +*/ +@(require_results) +stat_do_not_follow_links :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) { + return _lstat(name, allocator) +} + + +/* + Returns true if two `File_Info`s are equivalent. +*/ +@(require_results) +same_file :: proc(fi1, fi2: File_Info) -> bool { + return _same_file(fi1, fi2) +} + + +last_write_time :: modification_time +last_write_time_by_name :: modification_time_by_path + +/* + Returns the modification time of the file `f`. + The resolution of the timestamp is system-dependent. +*/ +@(require_results) +modification_time :: proc(f: ^File) -> (time.Time, Error) { + temp_allocator := TEMP_ALLOCATOR_GUARD({}) + fi, err := fstat(f, temp_allocator) + return fi.modification_time, err +} + +/* + Returns the modification time of the named file `path`. + The resolution of the timestamp is system-dependent. +*/ +@(require_results) +modification_time_by_path :: proc(path: string) -> (time.Time, Error) { + temp_allocator := TEMP_ALLOCATOR_GUARD({}) + fi, err := stat(path, temp_allocator) + return fi.modification_time, err +} + +is_reserved_name :: proc(path: string) -> bool { + return _is_reserved_name(path) +} \ No newline at end of file diff --git a/core/os/os2/stat_js.odin b/core/os/stat_js.odin similarity index 100% rename from core/os/os2/stat_js.odin rename to core/os/stat_js.odin diff --git a/core/os/os2/stat_linux.odin b/core/os/stat_linux.odin similarity index 100% rename from core/os/os2/stat_linux.odin rename to core/os/stat_linux.odin diff --git a/core/os/os2/stat_posix.odin b/core/os/stat_posix.odin similarity index 100% rename from core/os/os2/stat_posix.odin rename to core/os/stat_posix.odin diff --git a/core/os/os2/stat_wasi.odin b/core/os/stat_wasi.odin similarity index 100% rename from core/os/os2/stat_wasi.odin rename to core/os/stat_wasi.odin diff --git a/core/os/stat_windows.odin b/core/os/stat_windows.odin index 662c9f9e6..651029ac3 100644 --- a/core/os/stat_windows.odin +++ b/core/os/stat_windows.odin @@ -1,51 +1,82 @@ -package os +#+private +package os2 -import "core:time" import "base:runtime" +import "core:time" +import "core:strings" import win32 "core:sys/windows" -@(private, require_results) -full_path_from_name :: proc(name: string, allocator := context.allocator) -> (path: string, err: Errno) { - context.allocator = allocator - +_fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) { + if f == nil || (^File_Impl)(f.impl).fd == nil { + return + } + + path := _cleanpath_from_handle(f, allocator) or_return + + h := _handle(f) + switch win32.GetFileType(h) { + case win32.FILE_TYPE_PIPE, win32.FILE_TYPE_CHAR: + fi = File_Info { + fullpath = path, + name = basename(path), + type = file_type(h), + } + return + } + + return _file_info_from_get_file_information_by_handle(path, h, allocator) +} + +_stat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) { + return internal_stat(name, win32.FILE_FLAG_BACKUP_SEMANTICS, allocator) +} + +_lstat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) { + return internal_stat(name, win32.FILE_FLAG_BACKUP_SEMANTICS|win32.FILE_FLAG_OPEN_REPARSE_POINT, allocator) +} + +_same_file :: proc(fi1, fi2: File_Info) -> bool { + return fi1.fullpath == fi2.fullpath +} + +full_path_from_name :: proc(name: string, allocator: runtime.Allocator) -> (path: string, err: Error) { name := name if name == "" { name = "." } - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) - p := win32.utf8_to_utf16(name, context.temp_allocator) - buf := make([dynamic]u16, 100) - defer delete(buf) - for { - n := win32.GetFullPathNameW(cstring16(raw_data(p)), u32(len(buf)), cstring16(raw_data(buf)), nil) - if n == 0 { - return "", get_last_error() - } - if n <= u32(len(buf)) { - return win32.utf16_to_utf8(buf[:n], allocator) or_else "", nil - } - resize(&buf, len(buf)*2) - } - return + temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) + + p := win32_utf8_to_utf16(name, temp_allocator) or_return + + n := win32.GetFullPathNameW(cstring16(raw_data(p)), 0, nil, nil) + if n == 0 { + return "", _get_platform_error() + } + buf := make([]u16, n+1, temp_allocator) + n = win32.GetFullPathNameW(cstring16(raw_data(p)), u32(len(buf)), cstring16(raw_data(buf)), nil) + if n == 0 { + return "", _get_platform_error() + } + return win32_utf16_to_utf8(buf[:n], allocator) } -@(private, require_results) -_stat :: proc(name: string, create_file_attributes: u32, allocator := context.allocator) -> (fi: File_Info, e: Errno) { +internal_stat :: proc(name: string, create_file_attributes: u32, allocator: runtime.Allocator) -> (fi: File_Info, e: Error) { if len(name) == 0 { - return {}, ERROR_PATH_NOT_FOUND + return {}, .Not_Exist } + temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) - context.allocator = allocator - - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) - - wname := win32.utf8_to_wstring(fix_long_path(name), context.temp_allocator) + wname := _fix_long_path(name, temp_allocator) or_return fa: win32.WIN32_FILE_ATTRIBUTE_DATA ok := win32.GetFileAttributesExW(wname, win32.GetFileExInfoStandard, &fa) if ok && fa.dwFileAttributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 { // Not a symlink - return file_info_from_win32_file_attribute_data(&fa, name) + fi = _file_info_from_win32_file_attribute_data(&fa, name, allocator) or_return + if fi.type == .Undetermined { + fi.type = _file_type_from_create_file(wname, create_file_attributes) + } + return } err := 0 if ok else win32.GetLastError() @@ -54,65 +85,28 @@ _stat :: proc(name: string, create_file_attributes: u32, allocator := context.al fd: win32.WIN32_FIND_DATAW sh := win32.FindFirstFileW(wname, &fd) if sh == win32.INVALID_HANDLE_VALUE { - e = get_last_error() + e = _get_platform_error() return } win32.FindClose(sh) - return file_info_from_win32_find_data(&fd, name) + fi = _file_info_from_win32_find_data(&fd, name, allocator) or_return + if fi.type == .Undetermined { + fi.type = _file_type_from_create_file(wname, create_file_attributes) + } + return } h := win32.CreateFileW(wname, 0, 0, nil, win32.OPEN_EXISTING, create_file_attributes, nil) if h == win32.INVALID_HANDLE_VALUE { - e = get_last_error() + e = _get_platform_error() return } defer win32.CloseHandle(h) - return file_info_from_get_file_information_by_handle(name, h) + return _file_info_from_get_file_information_by_handle(name, h, allocator) } - -@(require_results) -lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Errno) { - attrs := win32.FILE_FLAG_BACKUP_SEMANTICS - attrs |= win32.FILE_FLAG_OPEN_REPARSE_POINT - return _stat(name, attrs, allocator) -} - -@(require_results) -stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Errno) { - attrs := win32.FILE_FLAG_BACKUP_SEMANTICS - return _stat(name, attrs, allocator) -} - -@(require_results) -fstat :: proc(fd: Handle, allocator := context.allocator) -> (fi: File_Info, err: Errno) { - if fd == 0 { - err = ERROR_INVALID_HANDLE - } - context.allocator = allocator - - path := cleanpath_from_handle(fd) or_return - defer if err != nil { - delete(path) - } - - h := win32.HANDLE(fd) - switch win32.GetFileType(h) { - case win32.FILE_TYPE_PIPE, win32.FILE_TYPE_CHAR: - fi.name = basename(path) - fi.mode |= file_type_mode(h) - err = nil - case: - fi = file_info_from_get_file_information_by_handle(path, h) or_return - } - fi.fullpath = path - return -} - - -@(private, require_results) -cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 { +_cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 { buf := buf N := 0 for c, i in buf { @@ -121,50 +115,59 @@ cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 { } buf = buf[:N] - if len(buf) >= 4 && buf[0] == '\\' && buf[1] == '\\' && buf[2] == '?' && buf[3] == '\\' { - buf = buf[4:] - - /* - NOTE(Jeroen): Properly handle UNC paths. - We need to turn `\\?\UNC\synology.local` into `\\synology.local`. - */ - if len(buf) >= 3 && buf[0] == 'U' && buf[1] == 'N' && buf[2] == 'C' { - buf = buf[2:] - buf[0] = '\\' + if len(buf) >= 4 { + if buf[0] == '\\' && + buf[1] == '\\' && + buf[2] == '?' && + buf[3] == '\\' { + buf = buf[4:] } } return buf } -@(private, require_results) -cleanpath_from_handle :: proc(fd: Handle) -> (s: string, err: Errno) { - runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator) - buf := cleanpath_from_handle_u16(fd, context.temp_allocator) or_return - return win32.utf16_to_utf8(buf, context.allocator) -} -@(private, require_results) -cleanpath_from_handle_u16 :: proc(fd: Handle, allocator: runtime.Allocator) -> ([]u16, Errno) { - if fd == 0 { - return nil, ERROR_INVALID_HANDLE +_cleanpath_from_handle :: proc(f: ^File, allocator: runtime.Allocator) -> (string, Error) { + if f == nil { + return "", nil } - h := win32.HANDLE(fd) + h := _handle(f) n := win32.GetFinalPathNameByHandleW(h, nil, 0, 0) if n == 0 { - return nil, get_last_error() + return "", _get_platform_error() } - buf := make([]u16, max(n, win32.DWORD(260))+1, allocator) - buf_len := win32.GetFinalPathNameByHandleW(h, cstring16(raw_data(buf)), n, 0) - return buf[:buf_len], nil -} -@(private, require_results) -cleanpath_from_buf :: proc(buf: []u16) -> string { - buf := buf - buf = cleanpath_strip_prefix(buf) - return win32.utf16_to_utf8(buf, context.allocator) or_else "" + + temp_allocator := TEMP_ALLOCATOR_GUARD({ allocator }) + + buf := make([]u16, max(n, 260)+1, temp_allocator) + n = win32.GetFinalPathNameByHandleW(h, cstring16(raw_data(buf)), u32(len(buf)), 0) + return _cleanpath_from_buf(string16(buf[:n]), allocator) +} + +_cleanpath_from_handle_u16 :: proc(f: ^File) -> ([]u16, Error) { + if f == nil { + return nil, nil + } + h := _handle(f) + + n := win32.GetFinalPathNameByHandleW(h, nil, 0, 0) + if n == 0 { + return nil, _get_platform_error() + } + + temp_allocator := TEMP_ALLOCATOR_GUARD({}) + + buf := make([]u16, max(n, 260)+1, temp_allocator) + n = win32.GetFinalPathNameByHandleW(h, cstring16(raw_data(buf)), u32(len(buf)), 0) + return _cleanpath_strip_prefix(buf[:n]), nil +} + +_cleanpath_from_buf :: proc(buf: string16, allocator: runtime.Allocator) -> (string, runtime.Allocator_Error) { + buf := transmute([]u16)buf + buf = _cleanpath_strip_prefix(buf) + return win32_utf16_to_utf8(buf, allocator) } -@(private, require_results) basename :: proc(name: string) -> (base: string) { name := name if len(name) > 3 && name[:3] == `\\?` { @@ -190,114 +193,201 @@ basename :: proc(name: string) -> (base: string) { return name } -@(private, require_results) -file_type_mode :: proc(h: win32.HANDLE) -> File_Mode { +file_type :: proc(h: win32.HANDLE) -> File_Type { switch win32.GetFileType(h) { - case win32.FILE_TYPE_PIPE: - return File_Mode_Named_Pipe - case win32.FILE_TYPE_CHAR: - return File_Mode_Device | File_Mode_Char_Device + case win32.FILE_TYPE_PIPE: return .Named_Pipe + case win32.FILE_TYPE_CHAR: return .Character_Device + case win32.FILE_TYPE_DISK: return .Regular } - return 0 + return .Undetermined } +_file_type_from_create_file :: proc(wname: win32.wstring, create_file_attributes: u32) -> File_Type { + h := win32.CreateFileW(wname, 0, 0, nil, win32.OPEN_EXISTING, create_file_attributes, nil) + if h == win32.INVALID_HANDLE_VALUE { + return .Undetermined + } + defer win32.CloseHandle(h) + return file_type(h) +} -@(private, require_results) -file_mode_from_file_attributes :: proc(FileAttributes: win32.DWORD, h: win32.HANDLE, ReparseTag: win32.DWORD) -> (mode: File_Mode) { - if FileAttributes & win32.FILE_ATTRIBUTE_READONLY != 0 { - mode |= 0o444 +_file_type_mode_from_file_attributes :: proc(file_attributes: win32.DWORD, h: win32.HANDLE, ReparseTag: win32.DWORD) -> (type: File_Type, mode: Permissions) { + if file_attributes & win32.FILE_ATTRIBUTE_READONLY != 0 { + mode += Permissions_Write_All } else { - mode |= 0o666 + mode += Permissions_Read_Write_All } is_sym := false - if FileAttributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 { + if file_attributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 { is_sym = false } else { is_sym = ReparseTag == win32.IO_REPARSE_TAG_SYMLINK || ReparseTag == win32.IO_REPARSE_TAG_MOUNT_POINT } if is_sym { - mode |= File_Mode_Sym_Link - } else { - if FileAttributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 { - mode |= 0o111 | File_Mode_Dir - } - - if h != nil { - mode |= file_type_mode(h) - } + type = .Symlink + } else if file_attributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 { + type = .Directory + mode += Permissions_Execute_All + } else if h != nil { + type = file_type(h) } - return } -@(private) -windows_set_file_info_times :: proc(fi: ^File_Info, d: ^$T) { - fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime)) - fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime)) - fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime)) +// a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC) +time_as_filetime :: #force_inline proc(t: time.Time) -> (ft: win32.LARGE_INTEGER) { + win := u64(t._nsec / 100) + 116444736000000000 + return win32.LARGE_INTEGER(win) } -@(private, require_results) -file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string) -> (fi: File_Info, e: Errno) { +filetime_as_time_li :: #force_inline proc(ft: win32.LARGE_INTEGER) -> (t: time.Time) { + return {_nsec=(i64(ft) - 116444736000000000) * 100} +} + +filetime_as_time_ft :: #force_inline proc(ft: win32.FILETIME) -> (t: time.Time) { + return filetime_as_time_li(win32.LARGE_INTEGER(ft.dwLowDateTime) + win32.LARGE_INTEGER(ft.dwHighDateTime) << 32) +} + +filetime_as_time :: proc{filetime_as_time_ft, filetime_as_time_li} + +_file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string, allocator: runtime.Allocator) -> (fi: File_Info, e: Error) { fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow) - - fi.mode |= file_mode_from_file_attributes(d.dwFileAttributes, nil, 0) - fi.is_dir = fi.mode & File_Mode_Dir != 0 - - windows_set_file_info_times(&fi, d) - - fi.fullpath, e = full_path_from_name(name) + type, mode := _file_type_mode_from_file_attributes(d.dwFileAttributes, nil, 0) + fi.type = type + fi.mode |= mode + fi.creation_time = filetime_as_time(d.ftCreationTime) + fi.modification_time = filetime_as_time(d.ftLastWriteTime) + fi.access_time = filetime_as_time(d.ftLastAccessTime) + fi.fullpath, e = full_path_from_name(name, allocator) fi.name = basename(fi.fullpath) - return } -@(private, require_results) -file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string) -> (fi: File_Info, e: Errno) { +_file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string, allocator: runtime.Allocator) -> (fi: File_Info, e: Error) { fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow) - - fi.mode |= file_mode_from_file_attributes(d.dwFileAttributes, nil, 0) - fi.is_dir = fi.mode & File_Mode_Dir != 0 - - windows_set_file_info_times(&fi, d) - - fi.fullpath, e = full_path_from_name(name) + type, mode := _file_type_mode_from_file_attributes(d.dwFileAttributes, nil, 0) + fi.type = type + fi.mode |= mode + fi.creation_time = filetime_as_time(d.ftCreationTime) + fi.modification_time = filetime_as_time(d.ftLastWriteTime) + fi.access_time = filetime_as_time(d.ftLastAccessTime) + fi.fullpath, e = full_path_from_name(name, allocator) fi.name = basename(fi.fullpath) - return } -@(private, require_results) -file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HANDLE) -> (File_Info, Errno) { +_file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HANDLE, allocator: runtime.Allocator) -> (File_Info, Error) { d: win32.BY_HANDLE_FILE_INFORMATION if !win32.GetFileInformationByHandle(h, &d) { - err := get_last_error() - return {}, err + return {}, _get_platform_error() } ti: win32.FILE_ATTRIBUTE_TAG_INFO if !win32.GetFileInformationByHandleEx(h, .FileAttributeTagInfo, &ti, size_of(ti)) { - err := get_last_error() - if err != ERROR_INVALID_PARAMETER { + err := _get_platform_error() + if perr, ok := is_platform_error(err); ok && perr != i32(win32.ERROR_INVALID_PARAMETER) { return {}, err } // Indicate this is a symlink on FAT file systems ti.ReparseTag = 0 } - fi: File_Info - fi.fullpath = path fi.name = basename(path) - fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow) - - fi.mode |= file_mode_from_file_attributes(ti.FileAttributes, h, ti.ReparseTag) - fi.is_dir = fi.mode & File_Mode_Dir != 0 - - windows_set_file_info_times(&fi, &d) - + fi.inode = u128(u64(d.nFileIndexHigh)<<32 + u64(d.nFileIndexLow)) + fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow) + type, mode := _file_type_mode_from_file_attributes(d.dwFileAttributes, h, 0) + fi.type = type + fi.mode |= mode + fi.creation_time = filetime_as_time(d.ftCreationTime) + fi.modification_time = filetime_as_time(d.ftLastWriteTime) + fi.access_time = filetime_as_time(d.ftLastAccessTime) return fi, nil } + +reserved_names := [?]string{ + "CON", "PRN", "AUX", "NUL", + "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9", + "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9", +} + +_is_reserved_name :: proc(path: string) -> bool { + if len(path) == 0 { + return false + } + for reserved in reserved_names { + if strings.equal_fold(path, reserved) { + return true + } + } + return false +} + +_volume_name_len :: proc(path: string) -> (length: int) { + if len(path) < 2 { + return 0 + } + + if path[1] == ':' { + switch path[0] { + case 'a'..='z', 'A'..='Z': + return 2 + } + } + + /* + See: URL: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx + Further allowed paths can be of the form of: + - \\server\share or \\server\share\more\path + - \\?\C:\... + - \\.\PhysicalDriveX + */ + // Any remaining kind of path has to start with two slashes. + if !_is_path_separator(path[0]) || !_is_path_separator(path[1]) { + return 0 + } + + // Device path. The volume name is the whole string + if len(path) >= 5 && path[2] == '.' && _is_path_separator(path[3]) { + return len(path) + } + + // We're a UNC share `\\host\share`, file namespace `\\?\C:` or UNC in file namespace `\\?\\host\share` + prefix := 2 + + // File namespace. + if len(path) >= 5 && path[2] == '?' && _is_path_separator(path[3]) { + if _is_path_separator(path[4]) { + // `\\?\\` UNC path in file namespace + prefix = 5 + } + + if len(path) >= 6 && path[5] == ':' { + switch path[4] { + case 'a'..='z', 'A'..='Z': + return 6 + case: + return 0 + } + } + } + + // UNC path, minimum version of the volume is `\\h\s` for host, share. + // Can also contain an IP address in the host position. + slash_count := 0 + for i in prefix.. 0 { + slash_count += 1 + + if slash_count == 2 { + return i + } + } + } + + return len(path) +} \ No newline at end of file diff --git a/core/os/os2/temp_file.odin b/core/os/temp_file.odin similarity index 100% rename from core/os/os2/temp_file.odin rename to core/os/temp_file.odin diff --git a/core/os/os2/temp_file_js.odin b/core/os/temp_file_js.odin similarity index 100% rename from core/os/os2/temp_file_js.odin rename to core/os/temp_file_js.odin diff --git a/core/os/os2/temp_file_linux.odin b/core/os/temp_file_linux.odin similarity index 100% rename from core/os/os2/temp_file_linux.odin rename to core/os/temp_file_linux.odin diff --git a/core/os/os2/temp_file_posix.odin b/core/os/temp_file_posix.odin similarity index 100% rename from core/os/os2/temp_file_posix.odin rename to core/os/temp_file_posix.odin diff --git a/core/os/os2/temp_file_wasi.odin b/core/os/temp_file_wasi.odin similarity index 100% rename from core/os/os2/temp_file_wasi.odin rename to core/os/temp_file_wasi.odin diff --git a/core/os/os2/temp_file_windows.odin b/core/os/temp_file_windows.odin similarity index 100% rename from core/os/os2/temp_file_windows.odin rename to core/os/temp_file_windows.odin diff --git a/core/os/os2/user.odin b/core/os/user.odin similarity index 100% rename from core/os/os2/user.odin rename to core/os/user.odin diff --git a/core/os/os2/user_posix.odin b/core/os/user_posix.odin similarity index 100% rename from core/os/os2/user_posix.odin rename to core/os/user_posix.odin diff --git a/core/os/os2/user_windows.odin b/core/os/user_windows.odin similarity index 100% rename from core/os/os2/user_windows.odin rename to core/os/user_windows.odin diff --git a/core/path/filepath/match.odin b/core/path/filepath/match.odin index 00f5bcc3f..178d5f986 100644 --- a/core/path/filepath/match.odin +++ b/core/path/filepath/match.odin @@ -2,7 +2,7 @@ #+build !js package filepath -import os "core:os/os2" +import "core:os" // match states whether "name" matches the shell pattern // Pattern syntax is: diff --git a/core/path/filepath/path.odin b/core/path/filepath/path.odin index 2f2f7996c..58dc06103 100644 --- a/core/path/filepath/path.odin +++ b/core/path/filepath/path.odin @@ -2,8 +2,8 @@ // To process paths such as URLs that depend on forward slashes regardless of the OS, use the slashpath package. package filepath -import os "core:os/os2" -import "core:strings" +import "core:os" +import "core:strings" SEPARATOR_CHARS :: `/\` diff --git a/core/path/filepath/walk.odin b/core/path/filepath/walk.odin index 00b063bc7..2e2a4ff54 100644 --- a/core/path/filepath/walk.odin +++ b/core/path/filepath/walk.odin @@ -2,7 +2,7 @@ #+build !js package filepath -import os "core:os/os2" +import "core:os" Walker :: os.Walker @@ -43,9 +43,9 @@ If an error occurred opening a directory, you may get zero'd info struct and Example: package main - import "core:fmt" - import "core:strings" - import os "core:os/os2" + import "core:fmt" + import "core:strings" + import "core:os" main :: proc() { w := os.walker_create("core") diff --git a/core/prof/spall/spall.odin b/core/prof/spall/spall.odin index b079c2eb2..281eaa82d 100644 --- a/core/prof/spall/spall.odin +++ b/core/prof/spall/spall.odin @@ -2,8 +2,8 @@ package spall import "base:intrinsics" -import os "core:os/os2" -import "core:time" +import "core:os" +import "core:time" // File Format diff --git a/core/terminal/internal_os.odin b/core/terminal/internal_os.odin index 841803766..127cbae54 100644 --- a/core/terminal/internal_os.odin +++ b/core/terminal/internal_os.odin @@ -3,9 +3,9 @@ #+private package terminal -import "base:runtime" -import os "core:os/os2" -import "core:strings" +import "base:runtime" +import "core:os" +import "core:strings" // Reference documentation: // diff --git a/core/terminal/terminal_posix.odin b/core/terminal/terminal_posix.odin index 751ef85cf..83e64c6d8 100644 --- a/core/terminal/terminal_posix.odin +++ b/core/terminal/terminal_posix.odin @@ -2,8 +2,8 @@ #+build linux, darwin, netbsd, openbsd, freebsd, haiku package terminal -import "base:runtime" -import os "core:os/os2" +import "base:runtime" +import "core:os" _is_terminal :: proc "contextless" (f: ^os.File) -> bool { return os.is_tty(f) diff --git a/core/terminal/terminal_windows.odin b/core/terminal/terminal_windows.odin index 78d21952b..6c77330b5 100644 --- a/core/terminal/terminal_windows.odin +++ b/core/terminal/terminal_windows.odin @@ -1,9 +1,9 @@ #+private package terminal -import "base:runtime" -import os "core:os/os2" -import "core:sys/windows" +import "base:runtime" +import "core:os" +import "core:sys/windows" _is_terminal :: proc "contextless" (f: ^os.File) -> bool { return os.is_tty(f) diff --git a/core/testing/runner.odin b/core/testing/runner.odin index 8873bc973..8f26aa6c6 100644 --- a/core/testing/runner.odin +++ b/core/testing/runner.odin @@ -10,24 +10,24 @@ package testing Feoramund: Total rewrite. */ -import "base:intrinsics" -import "base:runtime" -import "core:bytes" -@require import "core:encoding/base64" -@require import "core:encoding/json" -import "core:fmt" -import "core:io" -@require import "core:log" -import "core:math/rand" -import "core:mem" -import os "core:os/os2" -import "core:slice" -@require import "core:strings" -import "core:sync/chan" -import "core:terminal" -import "core:terminal/ansi" -import "core:thread" -import "core:time" +import "base:intrinsics" +import "base:runtime" +import "core:bytes" +@(require) import "core:encoding/base64" +@(require) import "core:encoding/json" +import "core:fmt" +import "core:io" +@(require) import "core:log" +import "core:math/rand" +import "core:mem" +import "core:os" +import "core:slice" +@(require) import "core:strings" +import "core:sync/chan" +import "core:terminal" +import "core:terminal/ansi" +import "core:thread" +import "core:time" // Specify how many threads to use when running tests. TEST_THREADS : int : #config(ODIN_TEST_THREADS, 0) diff --git a/core/testing/signal_handler_libc.odin b/core/testing/signal_handler_libc.odin index badee802d..fb19a0115 100644 --- a/core/testing/signal_handler_libc.odin +++ b/core/testing/signal_handler_libc.odin @@ -11,11 +11,11 @@ package testing blob1807: Windows Win32 API rewrite. */ -import "base:intrinsics" -import "core:c/libc" -import os "core:os/os2" -import "core:sync" -import "core:terminal/ansi" +import "base:intrinsics" +import "core:c/libc" +import "core:os" +import "core:sync" +import "core:terminal/ansi" @(private="file") stop_runner_flag: libc.sig_atomic_t diff --git a/core/text/i18n/i18_js.odin b/core/text/i18n/i18_js.odin index 743718942..73e8535a5 100644 --- a/core/text/i18n/i18_js.odin +++ b/core/text/i18n/i18_js.odin @@ -10,8 +10,6 @@ package i18n List of contributors: Jeroen van Rijn: Initial implementation. */ -import os "core:os/os2" - @(private) parse_qt :: proc { parse_qt_linguist_from_bytes } diff --git a/core/text/i18n/i18n_os.odin b/core/text/i18n/i18n_os.odin index db82a9cf6..7a7995612 100644 --- a/core/text/i18n/i18n_os.odin +++ b/core/text/i18n/i18n_os.odin @@ -11,7 +11,7 @@ package i18n Jeroen van Rijn: Initial implementation. */ import "base:runtime" -import os "core:os/os2" +import "core:os" @(private) read_file :: proc(filename: string, allocator: runtime.Allocator) -> (data: []u8, err: Error) { diff --git a/core/text/regex/common/os.odin b/core/text/regex/common/os.odin index 1d38d687c..bde57f77f 100644 --- a/core/text/regex/common/os.odin +++ b/core/text/regex/common/os.odin @@ -10,7 +10,7 @@ package regex_common Feoramund: Initial implementation. */ -@require import os "core:os/os2" +@require import "core:os" when ODIN_DEBUG_REGEX { debug_stream := os.stderr.stream diff --git a/core/text/table/doc.odin b/core/text/table/doc.odin index 29b60dbe2..d91763661 100644 --- a/core/text/table/doc.odin +++ b/core/text/table/doc.odin @@ -189,7 +189,7 @@ Example: import "core:fmt" import "core:io" - import os "core:os/os2" + import "core:os" import "core:text/table" scripts :: proc(w: io.Writer) { @@ -265,10 +265,10 @@ corners and dividers. Example: package main - import "core:fmt" - import "core:io" - import os "core:os/os2" - import "core:text/table" + import "core:fmt" + import "core:io" + import "core:os" + import "core:text/table" box_drawing :: proc(w: io.Writer) { t: table.Table diff --git a/core/text/table/utility.odin b/core/text/table/utility.odin index db5ae4602..675fa6b10 100644 --- a/core/text/table/utility.odin +++ b/core/text/table/utility.odin @@ -2,9 +2,9 @@ #+build !js package text_table -import "core:io" -import os "core:os/os2" -import "core:strings" +import "core:io" +import "core:os" +import "core:strings" stdio_writer :: proc() -> io.Writer { return os.to_stream(os.stdout) diff --git a/core/time/timezone/tz_os.odin b/core/time/timezone/tz_os.odin index 2ab7cfa6c..fae4980c3 100644 --- a/core/time/timezone/tz_os.odin +++ b/core/time/timezone/tz_os.odin @@ -2,7 +2,7 @@ #+build !js package timezone -import os "core:os/os2" +import "core:os" import "core:time/datetime" load_tzif_file :: proc(filename: string, region_name: string, allocator := context.allocator) -> (out: ^datetime.TZ_Region, ok: bool) { diff --git a/core/time/timezone/tz_unix.odin b/core/time/timezone/tz_unix.odin index 60a20e57c..3939b3265 100644 --- a/core/time/timezone/tz_unix.odin +++ b/core/time/timezone/tz_unix.odin @@ -2,10 +2,10 @@ #+private package timezone -import os "core:os/os2" -import "core:strings" -import "core:time/datetime" -import "core:path/filepath" +import "core:os" +import "core:strings" +import "core:time/datetime" +import "core:path/filepath" local_tz_name :: proc(allocator := context.allocator) -> (name: string, success: bool) { local_str, ok := os.lookup_env("TZ", allocator) diff --git a/core/unicode/tools/generate_entity_table.odin b/core/unicode/tools/generate_entity_table.odin index 02958ad26..54f73370c 100644 --- a/core/unicode/tools/generate_entity_table.odin +++ b/core/unicode/tools/generate_entity_table.odin @@ -1,7 +1,7 @@ package xml_example import "core:encoding/xml" -import os "core:os/os2" +import "core:os" import path "core:path/filepath" import "core:strings" import "core:strconv" diff --git a/examples/all/all_main.odin b/examples/all/all_main.odin index c5f627653..3576fc027 100644 --- a/examples/all/all_main.odin +++ b/examples/all/all_main.odin @@ -117,7 +117,7 @@ package all @(require) import "core:prof/spall" @(require) import "core:os" -@(require) import "core:os/os2" +@(require) import "core:os/old" @(require) import "core:path/slashpath" @(require) import "core:path/filepath" diff --git a/tests/core/encoding/hxa/test_core_hxa.odin b/tests/core/encoding/hxa/test_core_hxa.odin index 17b3ca619..a4fee030c 100644 --- a/tests/core/encoding/hxa/test_core_hxa.odin +++ b/tests/core/encoding/hxa/test_core_hxa.odin @@ -6,7 +6,7 @@ import "core:testing" TEAPOT_PATH :: ODIN_ROOT + "tests/core/assets/HXA/teapot.hxa" -import os "core:os/os2" +import "core:os" @test test_read :: proc(t: ^testing.T) { diff --git a/tests/core/flags/test_core_flags.odin b/tests/core/flags/test_core_flags.odin index 1aee7f69c..834f6b630 100644 --- a/tests/core/flags/test_core_flags.odin +++ b/tests/core/flags/test_core_flags.odin @@ -1,16 +1,16 @@ package test_core_flags -import "base:runtime" -import "core:bytes" -import "core:flags" -import "core:fmt" -@require import "core:log" -import "core:math" -@require import "core:net" -import os "core:os/os2" -import "core:strings" -import "core:testing" -import "core:time/datetime" +import "base:runtime" +import "core:bytes" +import "core:flags" +import "core:fmt" +@(require) import "core:log" +import "core:math" +@(require) import "core:net" +import "core:os" +import "core:strings" +import "core:testing" +import "core:time/datetime" Custom_Data :: struct { a: int, diff --git a/tests/core/io/test_core_io.odin b/tests/core/io/test_core_io.odin index eb4d79317..301e7bb94 100644 --- a/tests/core/io/test_core_io.odin +++ b/tests/core/io/test_core_io.odin @@ -1,12 +1,12 @@ package test_core_io -import "core:bufio" -import "core:bytes" -import "core:io" -import "core:log" -import os "core:os/os2" -import "core:strings" -import "core:testing" +import "core:bufio" +import "core:bytes" +import "core:io" +import "core:log" +import "core:os" +import "core:strings" +import "core:testing" Passed_Tests :: distinct io.Stream_Mode_Set diff --git a/tests/core/nbio/fs.odin b/tests/core/nbio/fs.odin index 6e079f96e..1b10c03c9 100644 --- a/tests/core/nbio/fs.odin +++ b/tests/core/nbio/fs.odin @@ -1,9 +1,9 @@ package tests_nbio -import "core:nbio" -import "core:testing" -import "core:time" -import os "core:os/os2" +import "core:nbio" +import "core:testing" +import "core:time" +import "core:os" @(test) close_invalid_handle :: proc(t: ^testing.T) { diff --git a/tests/core/nbio/nbio.odin b/tests/core/nbio/nbio.odin index 2f454f55b..6c3fd0e8c 100644 --- a/tests/core/nbio/nbio.odin +++ b/tests/core/nbio/nbio.odin @@ -1,11 +1,11 @@ package tests_nbio -import "core:log" -import "core:nbio" -import "core:testing" -import "core:thread" -import "core:time" -import os "core:os/os2" +import "core:log" +import "core:nbio" +import "core:testing" +import "core:thread" +import "core:time" +import "core:os" ev :: testing.expect_value e :: testing.expect diff --git a/tests/core/normal.odin b/tests/core/normal.odin index 6b31b9d56..4708ed700 100644 --- a/tests/core/normal.odin +++ b/tests/core/normal.odin @@ -37,7 +37,7 @@ download_assets :: proc "contextless" () { @(require) import "net" @(require) import "odin" @(require) import "os" -@(require) import "os/os2" +@(require) import "os/old" @(require) import "reflect" @(require) import "runtime" @(require) import "slice" diff --git a/tests/core/os/os2/dir.odin b/tests/core/os/dir.odin similarity index 82% rename from tests/core/os/os2/dir.odin rename to tests/core/os/dir.odin index 8ef333219..464abed98 100644 --- a/tests/core/os/os2/dir.odin +++ b/tests/core/os/dir.odin @@ -1,14 +1,14 @@ -package tests_core_os_os2 +package tests_core_os -import os "core:os/os2" -import "core:log" -import "core:slice" -import "core:testing" -import "core:strings" +import "core:os" +import "core:log" +import "core:slice" +import "core:testing" +import "core:strings" @(test) test_read_dir :: proc(t: ^testing.T) { - path, err_join := os.join_path({#directory, "../dir"}, context.allocator) + path, err_join := os.join_path({#directory, "dir"}, context.allocator) defer delete(path) fis, err_read := os.read_all_directory_by_path(path, context.allocator) @@ -17,7 +17,7 @@ test_read_dir :: proc(t: ^testing.T) { slice.sort_by_key(fis, proc(fi: os.File_Info) -> string { return fi.name }) if err_read == .Unsupported { - log.warn("os2 directory functionality is unsupported, skipping test") + log.warn("core:os directory functionality is unsupported, skipping test") return } @@ -34,7 +34,7 @@ test_read_dir :: proc(t: ^testing.T) { @(test) test_walker :: proc(t: ^testing.T) { - path, err := os.join_path({#directory, "../dir"}, context.allocator) + path, err := os.join_path({#directory, "dir"}, context.allocator) defer delete(path) testing.expect_value(t, err, nil) @@ -46,7 +46,7 @@ test_walker :: proc(t: ^testing.T) { @(test) test_walker_file :: proc(t: ^testing.T) { - path, err_join := os.join_path({#directory, "../dir"}, context.allocator) + path, err_join := os.join_path({#directory, "dir"}, context.allocator) defer delete(path) testing.expect_value(t, err_join, nil) @@ -95,7 +95,7 @@ test_walker_internal :: proc(t: ^testing.T, w: ^os.Walker) { } if _, err := os.walker_error(w); err == .Unsupported { - log.warn("os2 directory functionality is unsupported, skipping test") + log.warn("core:os directory functionality is unsupported, skipping test") return } diff --git a/tests/core/os/os2/file.odin b/tests/core/os/file.odin similarity index 90% rename from tests/core/os/os2/file.odin rename to tests/core/os/file.odin index 0152a2008..aed57c26c 100644 --- a/tests/core/os/os2/file.odin +++ b/tests/core/os/file.odin @@ -1,7 +1,7 @@ -package tests_core_os_os2 +package tests_core_os -import os "core:os/os2" -import "core:testing" +import "core:os" +import "core:testing" @(test) test_clone :: proc(t: ^testing.T) { diff --git a/tests/core/os/os.odin b/tests/core/os/old/os.odin similarity index 97% rename from tests/core/os/os.odin rename to tests/core/os/old/os.odin index 1510bad31..9925cf708 100644 --- a/tests/core/os/os.odin +++ b/tests/core/os/old/os.odin @@ -1,8 +1,8 @@ -package test_core_os +package test_core_os_old import "core:c/libc" import win32 "core:sys/windows" -import "core:os" +import os "core:os/old" import "core:slice" import "core:testing" import "core:log" diff --git a/tests/core/os/os2/path.odin b/tests/core/os/path.odin similarity index 97% rename from tests/core/os/os2/path.odin rename to tests/core/os/path.odin index 868023c86..cdfaed56f 100644 --- a/tests/core/os/os2/path.odin +++ b/tests/core/os/path.odin @@ -1,11 +1,11 @@ -package tests_core_os_os2 +package tests_core_os -import "core:fmt" -import os "core:os/os2" -import "core:log" -import "core:testing" -import "core:slice" -import "core:strings" +import "core:fmt" +import "core:os" +import "core:log" +import "core:testing" +import "core:slice" +import "core:strings" @(test) test_executable :: proc(t: ^testing.T) { @@ -351,12 +351,12 @@ glob_tests := []Glob_Test{ err = {}, }, { - pattern = ODIN_ROOT + "tests/core/os/os2/*.odin", + pattern = ODIN_ROOT + "tests/core/os/*.odin", matches = { - ODIN_ROOT + "tests/core/os/os2/dir.odin", - ODIN_ROOT + "tests/core/os/os2/file.odin", - ODIN_ROOT + "tests/core/os/os2/path.odin", - ODIN_ROOT + "tests/core/os/os2/process.odin", + ODIN_ROOT + "tests/core/os/dir.odin", + ODIN_ROOT + "tests/core/os/file.odin", + ODIN_ROOT + "tests/core/os/path.odin", + ODIN_ROOT + "tests/core/os/process.odin", }, err = {}, }, diff --git a/tests/core/os/os2/process.odin b/tests/core/os/process.odin similarity index 84% rename from tests/core/os/os2/process.odin rename to tests/core/os/process.odin index c530b4c79..adb65e95f 100644 --- a/tests/core/os/os2/process.odin +++ b/tests/core/os/process.odin @@ -1,9 +1,9 @@ #+build !windows -package tests_core_os_os2 +package tests_core_os -import os "core:os/os2" -import "core:log" -import "core:testing" +import "core:os" +import "core:log" +import "core:testing" @(test) test_process_exec :: proc(t: ^testing.T) { diff --git a/tests/core/sys/kqueue/structs.odin b/tests/core/sys/kqueue/structs.odin index edf1fdd1e..15ec3f841 100644 --- a/tests/core/sys/kqueue/structs.odin +++ b/tests/core/sys/kqueue/structs.odin @@ -1,9 +1,9 @@ #+build darwin, freebsd, openbsd, netbsd package tests_core_sys_kqueue -import "core:strings" -import "core:testing" -import os "core:os/os2" +import "core:strings" +import "core:testing" +import "core:os" @(test) structs :: proc(t: ^testing.T) { diff --git a/tests/documentation/documentation_tester.odin b/tests/documentation/documentation_tester.odin index 6694de709..be59d9b4d 100644 --- a/tests/documentation/documentation_tester.odin +++ b/tests/documentation/documentation_tester.odin @@ -1,6 +1,6 @@ package documentation_tester -import os "core:os/os2" +import "core:os" import "core:fmt" import "core:strings" import "core:odin/ast" @@ -267,7 +267,7 @@ write_test_suite :: proc(example_tests: []Example_Test) { `#+private package documentation_verification -import os "core:os/os2" +import "core:os" import "core:mem" import "core:io" import "core:fmt" diff --git a/vendor/OpenGL/helpers.odin b/vendor/OpenGL/helpers.odin index bce60ee80..9c9c74d09 100644 --- a/vendor/OpenGL/helpers.odin +++ b/vendor/OpenGL/helpers.odin @@ -3,7 +3,7 @@ package vendor_gl // Helper for loading shaders into a program -import os "core:os/os2" +import "core:os" import "core:fmt" import "core:strings" @(require) import "core:time" diff --git a/vendor/fontstash/fontstash_os.odin b/vendor/fontstash/fontstash_os.odin index 8c259412d..d04df044c 100644 --- a/vendor/fontstash/fontstash_os.odin +++ b/vendor/fontstash/fontstash_os.odin @@ -1,8 +1,8 @@ #+build !js package fontstash -import "core:log" -import os "core:os/os2" +import "core:log" +import "core:os" // 'fontIndex' controls which font you want to load within a multi-font format such // as TTC. Leave it as zero if you are loading a single-font format such as TTF. diff --git a/vendor/libc-shim/stdio_os.odin b/vendor/libc-shim/stdio_os.odin index db40fb250..f6d30a227 100644 --- a/vendor/libc-shim/stdio_os.odin +++ b/vendor/libc-shim/stdio_os.odin @@ -2,9 +2,9 @@ #+build !js package odin_libc -import "core:io" -import "core:c" -import os "core:os/os2" +import "core:io" +import "core:c" +import "core:os" _fopen :: proc(path, _mode: cstring) -> FILE { flags: os.File_Flags