diff --git a/core/os/os2/env_windows.odin b/core/os/os2/env_windows.odin index 2ce755203..52c6c0f55 100644 --- a/core/os/os2/env_windows.odin +++ b/core/os/os2/env_windows.odin @@ -2,8 +2,9 @@ package os2 import win32 "core:sys/windows" +import "core:runtime" -_lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { +_lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) { if key == "" { return } @@ -17,7 +18,7 @@ _lookup_env :: proc(key: string, allocator := context.allocator) -> (value: stri } return "", true } - b := make([]u16, n+1, context.temp_allocator) + b := make([]u16, n+1, _temp_allocator()) n = win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b))) if n == 0 { @@ -25,6 +26,7 @@ _lookup_env :: proc(key: string, allocator := context.allocator) -> (value: stri if err == win32.ERROR_ENVVAR_NOT_FOUND { return "", false } + return "", false } value = win32.utf16_to_utf8(b[:n], allocator) @@ -45,7 +47,7 @@ _unset_env :: proc(key: string) -> bool { } _clear_env :: proc() { - envs := environ(context.temp_allocator) + envs := environ(_temp_allocator()) for env in envs { for j in 1.. bool { } _mkdir :: proc(name: string, perm: File_Mode) -> Error { + if !win32.CreateDirectoryW(_fix_long_path(name), nil) { + return _get_platform_error() + } return nil } _mkdir_all :: proc(path: string, perm: File_Mode) -> Error { - // TODO(bill): _mkdir_all for windows + fix_root_directory :: proc(p: string) -> (s: string, allocated: bool, err: runtime.Allocator_Error) { + if len(p) == len(`\\?\c:`) { + if is_path_separator(p[0]) && is_path_separator(p[1]) && p[2] == '?' && is_path_separator(p[3]) && p[5] == ':' { + s = strings.concatenate_safe({p, `\`}, _file_allocator()) or_return + allocated = true + return + } + } + return p, false, nil + } + + dir, err := stat(path, _temp_allocator()) + if err == nil { + if dir.is_dir { + return nil + } + return .Exist + } + + i := len(path) + for i > 0 && is_path_separator(path[i-1]) { + i -= 1 + } + + j := i + for j > 0 && !is_path_separator(path[j-1]) { + j -= 1 + } + + if j > 1 { + new_path, allocated := fix_root_directory(path[:j-1]) or_return + defer if allocated { + delete(new_path, _file_allocator()) + } + mkdir_all(new_path, perm) or_return + } + + err = mkdir(path, perm) + if err != nil { + dir1, err1 := lstat(path, _temp_allocator()) + if err1 == nil && dir1.is_dir { + return nil + } + return err + } return nil } @@ -24,11 +73,13 @@ _remove_all :: proc(path: string) -> Error { return nil } -_getwd :: proc(allocator := context.allocator) -> (dir: string, err: Error) { +_getwd :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { + // TODO(bill) return "", nil } _setwd :: proc(dir: string) -> (err: Error) { + // TODO(bill) return nil } @@ -75,7 +126,7 @@ _fix_long_path_internal :: proc(path: string) -> string { } PREFIX :: `\\?` - path_buf := make([]byte, len(PREFIX)+len(path)+1, context.temp_allocator) + path_buf := make([]byte, len(PREFIX)+len(path)+1, _temp_allocator()) copy(path_buf, PREFIX) n := len(path) r, w := 0, len(PREFIX) diff --git a/core/os/os2/pipe_windows.odin b/core/os/os2/pipe_windows.odin index ddb54f80c..bab8b44f5 100644 --- a/core/os/os2/pipe_windows.odin +++ b/core/os/os2/pipe_windows.odin @@ -6,7 +6,7 @@ import win32 "core:sys/windows" _pipe :: proc() -> (r, w: ^File, err: Error) { p: [2]win32.HANDLE if !win32.CreatePipe(&p[0], &p[1], nil, 0) { - return nil, nil, Platform_Error{i32(win32.GetLastError())} + return nil, nil, _get_platform_error() } return new_file(uintptr(p[0]), ""), new_file(uintptr(p[1]), ""), nil } diff --git a/core/os/os2/stat.odin b/core/os/os2/stat.odin index 63f5a17e8..24a01fb0a 100644 --- a/core/os/os2/stat.odin +++ b/core/os/os2/stat.odin @@ -1,6 +1,7 @@ package os2 import "core:time" +import "core:runtime" File_Info :: struct { fullpath: string, @@ -13,26 +14,26 @@ File_Info :: struct { access_time: time.Time, } -file_info_slice_delete :: proc(infos: []File_Info, allocator := context.allocator) { +file_info_slice_delete :: proc(infos: []File_Info, allocator: runtime.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) { +file_info_delete :: proc(fi: File_Info, allocator: runtime.Allocator) { delete(fi.fullpath, allocator) } -fstat :: proc(f: ^File, allocator := context.allocator) -> (File_Info, Error) { +fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) { return _fstat(f, allocator) } -stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error) { +stat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) { return _stat(name, allocator) } -lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error) { +lstat :: proc(name: string, allocator: runtime.Allocator) -> (File_Info, Error) { return _lstat(name, allocator) } diff --git a/core/os/os2/stat_windows.odin b/core/os/os2/stat_windows.odin index a79e5aae2..603343a18 100644 --- a/core/os/os2/stat_windows.odin +++ b/core/os/os2/stat_windows.odin @@ -1,22 +1,22 @@ //+private package os2 +import "core:runtime" import "core:time" import "core:strings" import win32 "core:sys/windows" -_fstat :: proc(f: ^File, allocator := context.allocator) -> (File_Info, Error) { +_fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) { if f == nil || f.impl.fd == nil { - return {}, .Invalid_Argument + return {}, nil } - context.allocator = allocator - path, err := _cleanpath_from_handle(f) + path, err := _cleanpath_from_handle(f, allocator) if err != nil { return {}, err } - h := win32.HANDLE(f.impl.fd) + h := _handle(f) switch win32.GetFileType(h) { case win32.FILE_TYPE_PIPE, win32.FILE_TYPE_CHAR: fi: File_Info @@ -26,13 +26,13 @@ _fstat :: proc(f: ^File, allocator := context.allocator) -> (File_Info, Error) { return fi, nil } - return _file_info_from_get_file_information_by_handle(path, h) + return _file_info_from_get_file_information_by_handle(path, h, allocator) } -_stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error) { - return internal_stat(name, win32.FILE_FLAG_BACKUP_SEMANTICS) +_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 := context.allocator) -> (File_Info, Error) { - return internal_stat(name, win32.FILE_FLAG_BACKUP_SEMANTICS|win32.FILE_FLAG_OPEN_REPARSE_POINT) +_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 @@ -40,50 +40,38 @@ _same_file :: proc(fi1, fi2: File_Info) -> bool { -_stat_errno :: proc(errno: win32.DWORD) -> Error { - return Platform_Error{i32(errno)} -} - -full_path_from_name :: proc(name: string, allocator := context.allocator) -> (path: string, err: Error) { - context.allocator = allocator - +full_path_from_name :: proc(name: string, allocator: runtime.Allocator) -> (path: string, err: Error) { name := name if name == "" { name = "." } - p := win32.utf8_to_utf16(name, context.temp_allocator) - buf := make([dynamic]u16, 100) - for { - n := win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil) - if n == 0 { - delete(buf) - return "", _stat_errno(win32.GetLastError()) - } - if n <= u32(len(buf)) { - return win32.utf16_to_utf8(buf[:n]), nil - } - resize(&buf, len(buf)*2) - } + p := win32.utf8_to_utf16(name, _temp_allocator()) - return + n := win32.GetFullPathNameW(raw_data(p), 0, nil, nil) + if n == 0 { + return "", _get_platform_error() + } + buf := make([]u16, n+1, _temp_allocator()) + n = win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil) + if n == 0 { + return "", _get_platform_error() + } + return win32.utf16_to_utf8(buf[:n], allocator), nil } -internal_stat :: proc(name: string, create_file_attributes: u32, allocator := context.allocator) -> (fi: File_Info, e: Error) { +internal_stat :: proc(name: string, create_file_attributes: u32, allocator: runtime.Allocator) -> (fi: File_Info, e: Error) { if len(name) == 0 { return {}, .Not_Exist } - context.allocator = allocator - - wname := _fix_long_path(name) 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) + return _file_info_from_win32_file_attribute_data(&fa, name, allocator) } err := 0 if ok else win32.GetLastError() @@ -97,7 +85,7 @@ internal_stat :: proc(name: string, create_file_attributes: u32, allocator := co } win32.FindClose(sh) - return _file_info_from_win32_find_data(&fd, name) + return _file_info_from_win32_find_data(&fd, name, allocator) } h := win32.CreateFileW(wname, 0, 0, nil, win32.OPEN_EXISTING, create_file_attributes, nil) @@ -106,7 +94,7 @@ internal_stat :: proc(name: string, create_file_attributes: u32, allocator := co 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) } @@ -131,56 +119,40 @@ _cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 { } -_cleanpath_from_handle :: proc(f: ^File) -> (string, Error) { +_cleanpath_from_handle :: proc(f: ^File, allocator: runtime.Allocator) -> (string, Error) { if f == nil || f.impl.fd == nil { - return "", .Invalid_Argument + return "", nil } - h := win32.HANDLE(f.impl.fd) + h := _handle(f) - MAX_PATH := win32.DWORD(260) + 1 - buf: []u16 - for { - buf = make([]u16, MAX_PATH, context.temp_allocator) - err := win32.GetFinalPathNameByHandleW(h, raw_data(buf), MAX_PATH, 0) - switch err { - case win32.ERROR_PATH_NOT_FOUND, win32.ERROR_INVALID_PARAMETER: - return "", _stat_errno(err) - case win32.ERROR_NOT_ENOUGH_MEMORY: - MAX_PATH = MAX_PATH*2 + 1 - continue - } - break + n := win32.GetFinalPathNameByHandleW(h, nil, 0, 0) + if n == 0 { + return "", _get_platform_error() } - return _cleanpath_from_buf(buf), nil + buf := make([]u16, max(n, 260)+1, _temp_allocator()) + n = win32.GetFinalPathNameByHandleW(h, raw_data(buf), u32(len(buf)), 0) + return _cleanpath_from_buf(buf[:n], allocator), nil } _cleanpath_from_handle_u16 :: proc(f: ^File) -> ([]u16, Error) { if f == nil || f.impl.fd == nil { - return nil, .Invalid_Argument + return nil, nil } - h := win32.HANDLE(f.impl.fd) + h := _handle(f) - MAX_PATH := win32.DWORD(260) + 1 - buf: []u16 - for { - buf = make([]u16, MAX_PATH, context.temp_allocator) - err := win32.GetFinalPathNameByHandleW(h, raw_data(buf), MAX_PATH, 0) - switch err { - case win32.ERROR_PATH_NOT_FOUND, win32.ERROR_INVALID_PARAMETER: - return nil, _stat_errno(err) - case win32.ERROR_NOT_ENOUGH_MEMORY: - MAX_PATH = MAX_PATH*2 + 1 - continue - } - break + n := win32.GetFinalPathNameByHandleW(h, nil, 0, 0) + if n == 0 { + return nil, _get_platform_error() } - return _cleanpath_strip_prefix(buf), nil + buf := make([]u16, max(n, 260)+1, _temp_allocator()) + n = win32.GetFinalPathNameByHandleW(h, raw_data(buf), u32(len(buf)), 0) + return _cleanpath_strip_prefix(buf[:n]), nil } -_cleanpath_from_buf :: proc(buf: []u16) -> string { +_cleanpath_from_buf :: proc(buf: []u16, allocator: runtime.Allocator) -> string { buf := buf buf = _cleanpath_strip_prefix(buf) - return win32.utf16_to_utf8(buf, context.allocator) + return win32.utf16_to_utf8(buf, allocator) } @@ -252,7 +224,7 @@ _file_mode_from_file_attributes :: proc(FileAttributes: win32.DWORD, h: win32.HA } -_file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string) -> (fi: File_Info, e: Error) { +_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) @@ -262,14 +234,14 @@ _file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE 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)) - fi.fullpath, e = full_path_from_name(name) + 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) -> (fi: File_Info, e: Error) { +_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) @@ -279,17 +251,17 @@ _file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string 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)) - fi.fullpath, e = full_path_from_name(name) + 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) -> (File_Info, Error) { +_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 {}, _stat_errno(win32.GetLastError()) + return {}, _get_platform_error() } @@ -297,7 +269,7 @@ _file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HA if !win32.GetFileInformationByHandleEx(h, .FileAttributeTagInfo, &ti, size_of(ti)) { err := win32.GetLastError() if err != win32.ERROR_INVALID_PARAMETER { - return {}, _stat_errno(err) + return {}, Platform_Error(err) } // Indicate this is a symlink on FAT file systems ti.ReparseTag = 0 diff --git a/core/os/os2/temp_file.odin b/core/os/os2/temp_file.odin index 90131699d..faf176de1 100644 --- a/core/os/os2/temp_file.odin +++ b/core/os/os2/temp_file.odin @@ -1,14 +1,15 @@ package os2 +import "core:runtime" create_temp :: proc(dir, pattern: string) -> (^File, Error) { return _create_temp(dir, pattern) } -mkdir_temp :: proc(dir, pattern: string, allocator := context.allocator) -> (string, Error) { - return _mkdir_temp(dir, pattern) +mkdir_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) -> (string, Error) { + return _mkdir_temp(dir, pattern, allocator) } -temp_dir :: proc(allocator := context.allocator) -> string { +temp_dir :: proc(allocator: runtime.Allocator) -> string { return _temp_dir(allocator) } diff --git a/core/os/os2/temp_file_windows.odin b/core/os/os2/temp_file_windows.odin index 17967393a..a7587988b 100644 --- a/core/os/os2/temp_file_windows.odin +++ b/core/os/os2/temp_file_windows.odin @@ -1,29 +1,29 @@ //+private package os2 +import "core:runtime" import win32 "core:sys/windows" _create_temp :: proc(dir, pattern: string) -> (^File, Error) { return nil, nil } -_mkdir_temp :: proc(dir, pattern: string, allocator := context.allocator) -> (string, Error) { +_mkdir_temp :: proc(dir, pattern: string, allocator: runtime.Allocator) -> (string, Error) { return "", nil } -_temp_dir :: proc(allocator := context.allocator) -> string { - b := make([dynamic]u16, u32(win32.MAX_PATH), context.temp_allocator) - for { - n := win32.GetTempPathW(u32(len(b)), raw_data(b)) - if n > u32(len(b)) { - resize(&b, int(n)) - continue - } - if n == 3 && b[1] == ':' && b[2] == '\\' { - - } else if n > 0 && b[n-1] == '\\' { - n -= 1 - } - return win32.utf16_to_utf8(b[:n], allocator) +_temp_dir :: proc(allocator: runtime.Allocator) -> string { + n := win32.GetTempPathW(0, nil) + if n == 0 { + return "" } + b := make([]u16, max(win32.MAX_PATH, n), _temp_allocator()) + n = win32.GetTempPathW(u32(len(b)), raw_data(b)) + + if n == 3 && b[1] == ':' && b[2] == '\\' { + + } else if n > 0 && b[n-1] == '\\' { + n -= 1 + } + return win32.utf16_to_utf8(b[:n], allocator) } diff --git a/core/os/os2/user.odin b/core/os/os2/user.odin index 976e61bb1..1fb653b85 100644 --- a/core/os/os2/user.odin +++ b/core/os/os2/user.odin @@ -1,18 +1,19 @@ package os2 import "core:strings" +import "core:runtime" -user_cache_dir :: proc(allocator := context.allocator) -> (dir: string, is_defined: bool) { +user_cache_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { #partial switch ODIN_OS { case .Windows: dir = get_env("LocalAppData") if dir != "" { - dir = strings.clone(dir, allocator) + dir = strings.clone_safe(dir, allocator) or_return } case .Darwin: dir = get_env("HOME") if dir != "" { - dir = strings.concatenate({dir, "/Library/Caches"}, allocator) + dir = strings.concatenate_safe({dir, "/Library/Caches"}, allocator) or_return } case: // All other UNIX systems dir = get_env("XDG_CACHE_HOME") @@ -21,24 +22,26 @@ user_cache_dir :: proc(allocator := context.allocator) -> (dir: string, is_defin if dir == "" { return } - dir = strings.concatenate({dir, "/.cache"}, allocator) + dir = strings.concatenate_safe({dir, "/.cache"}, allocator) or_return } } - is_defined = dir != "" + if dir == "" { + err = .Invalid_Path + } return } -user_config_dir :: proc(allocator := context.allocator) -> (dir: string, is_defined: bool) { +user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) { #partial switch ODIN_OS { case .Windows: dir = get_env("AppData") if dir != "" { - dir = strings.clone(dir, allocator) + dir = strings.clone_safe(dir, allocator) or_return } case .Darwin: dir = get_env("HOME") if dir != "" { - dir = strings.concatenate({dir, "/Library/Application Support"}, allocator) + dir = strings.concatenate_safe({dir, "/Library/Application Support"}, allocator) or_return } case: // All other UNIX systems dir = get_env("XDG_CACHE_HOME") @@ -47,22 +50,24 @@ user_config_dir :: proc(allocator := context.allocator) -> (dir: string, is_defi if dir == "" { return } - dir = strings.concatenate({dir, "/.config"}, allocator) + dir = strings.concatenate_safe({dir, "/.config"}, allocator) or_return } } - is_defined = dir != "" + if dir == "" { + err = .Invalid_Path + } return } -user_home_dir :: proc() -> (dir: string, is_defined: bool) { +user_home_dir :: proc() -> (dir: string, err: Error) { env := "HOME" #partial switch ODIN_OS { case .Windows: env = "USERPROFILE" } if v := get_env(env); v != "" { - return v, true + return v, nil } - return "", false + return "", .Invalid_Path } diff --git a/core/os/stat_unix.odin b/core/os/stat_unix.odin index 395d2e73e..dae7ab2fb 100644 --- a/core/os/stat_unix.odin +++ b/core/os/stat_unix.odin @@ -119,7 +119,6 @@ lstat :: proc(name: string, allocator := context.allocator) -> (fi: File_Info, e } stat :: proc(name: string, allocator := context.allocator) -> (fi: File_Info, err: Errno) { - context.allocator = allocator s: OS_Stat diff --git a/core/sys/windows/types.odin b/core/sys/windows/types.odin index 6770e7a95..4f594e22d 100644 --- a/core/sys/windows/types.odin +++ b/core/sys/windows/types.odin @@ -1140,6 +1140,7 @@ ERROR_BROKEN_PIPE: DWORD : 109 ERROR_CALL_NOT_IMPLEMENTED: DWORD : 120 ERROR_INSUFFICIENT_BUFFER: DWORD : 122 ERROR_INVALID_NAME: DWORD : 123 +ERROR_BAD_ARGUMENTS: DWORD: 160 ERROR_LOCK_FAILED: DWORD : 167 ERROR_ALREADY_EXISTS: DWORD : 183 ERROR_NO_DATA: DWORD : 232