mirror of
https://github.com/odin-lang/Odin.git
synced 2026-04-19 04:50:29 +00:00
Update error handling for os2 on windows
This commit is contained in:
@@ -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..<len(env) {
|
||||
if env[j] == '=' {
|
||||
@@ -56,7 +58,7 @@ _clear_env :: proc() {
|
||||
}
|
||||
}
|
||||
|
||||
_environ :: proc(allocator := context.allocator) -> []string {
|
||||
_environ :: proc(allocator: runtime.Allocator) -> []string {
|
||||
envs := win32.GetEnvironmentStringsW()
|
||||
if envs == nil {
|
||||
return nil
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package os2
|
||||
|
||||
import "core:io"
|
||||
import "core:runtime"
|
||||
|
||||
General_Error :: enum u32 {
|
||||
Invalid_Argument,
|
||||
None,
|
||||
|
||||
Permission_Denied,
|
||||
Exist,
|
||||
@@ -18,13 +19,12 @@ General_Error :: enum u32 {
|
||||
Unsupported,
|
||||
}
|
||||
|
||||
Platform_Error :: struct {
|
||||
err: i32,
|
||||
}
|
||||
Platform_Error :: enum i32 {None=0}
|
||||
|
||||
Error :: union {
|
||||
Error :: union #shared_nil {
|
||||
General_Error,
|
||||
io.Error,
|
||||
runtime.Allocator_Error,
|
||||
Platform_Error,
|
||||
}
|
||||
#assert(size_of(Error) == size_of(u64))
|
||||
@@ -33,36 +33,55 @@ Error :: union {
|
||||
|
||||
is_platform_error :: proc(ferr: Error) -> (err: i32, ok: bool) {
|
||||
v := ferr.(Platform_Error) or_else {}
|
||||
return v.err, v.err != 0
|
||||
return i32(v), i32(v) != 0
|
||||
}
|
||||
|
||||
|
||||
error_string :: proc(ferr: Error) -> string {
|
||||
switch ferr {
|
||||
case nil: return ""
|
||||
case .Invalid_Argument: return "invalid argument"
|
||||
case .Permission_Denied: return "permission denied"
|
||||
case .Exist: return "file already exists"
|
||||
case .Not_Exist: return "file does not exist"
|
||||
case .Closed: return "file already closed"
|
||||
case .Timeout: return "i/o timeout"
|
||||
case .EOF: return "eof"
|
||||
case .Unexpected_EOF: return "unexpected eof"
|
||||
case .Short_Write: return "short write"
|
||||
case .Invalid_Write: return "invalid write result"
|
||||
case .Short_Buffer: return "short buffer"
|
||||
case .No_Progress: return "multiple read calls return no data or error"
|
||||
case .Invalid_Whence: return "invalid whence"
|
||||
case .Invalid_Offset: return "invalid offset"
|
||||
case .Invalid_Unread: return "invalid unread"
|
||||
case .Negative_Read: return "negative read"
|
||||
case .Negative_Write: return "negative write"
|
||||
case .Negative_Count: return "negative count"
|
||||
case .Buffer_Full: return "buffer full"
|
||||
if ferr == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if errno, ok := is_platform_error(ferr); ok {
|
||||
return _error_string(errno)
|
||||
switch e in ferr {
|
||||
case General_Error:
|
||||
switch e {
|
||||
case .None: return ""
|
||||
case .Permission_Denied: return "permission denied"
|
||||
case .Exist: return "file already exists"
|
||||
case .Not_Exist: return "file does not exist"
|
||||
case .Closed: return "file already closed"
|
||||
case .Timeout: return "i/o timeout"
|
||||
case .Invalid_File: return "invalid file"
|
||||
case .Invalid_Path: return "invalid path"
|
||||
case .Unsupported: return "unsupported"
|
||||
}
|
||||
case io.Error:
|
||||
switch e {
|
||||
case .None: return ""
|
||||
case .EOF: return "eof"
|
||||
case .Unexpected_EOF: return "unexpected eof"
|
||||
case .Short_Write: return "short write"
|
||||
case .Invalid_Write: return "invalid write result"
|
||||
case .Short_Buffer: return "short buffer"
|
||||
case .No_Progress: return "multiple read calls return no data or error"
|
||||
case .Invalid_Whence: return "invalid whence"
|
||||
case .Invalid_Offset: return "invalid offset"
|
||||
case .Invalid_Unread: return "invalid unread"
|
||||
case .Negative_Read: return "negative read"
|
||||
case .Negative_Write: return "negative write"
|
||||
case .Negative_Count: return "negative count"
|
||||
case .Buffer_Full: return "buffer full"
|
||||
case .Unknown, .Empty: //
|
||||
}
|
||||
case runtime.Allocator_Error:
|
||||
switch e {
|
||||
case .None: return ""
|
||||
case .Out_Of_Memory: return "out of memory"
|
||||
case .Invalid_Pointer: return "invalid allocator pointer"
|
||||
case .Invalid_Argument: return "invalid allocator argument"
|
||||
case .Mode_Not_Implemented: return "allocator mode not implemented"
|
||||
}
|
||||
case Platform_Error:
|
||||
return _error_string(i32(e))
|
||||
}
|
||||
|
||||
return "unknown error"
|
||||
|
||||
@@ -12,3 +12,49 @@ _error_string :: proc(errno: i32) -> string {
|
||||
// FormatMessageW
|
||||
return ""
|
||||
}
|
||||
|
||||
_get_platform_error :: proc() -> Error {
|
||||
err := win32.GetLastError()
|
||||
if err == 0 {
|
||||
return nil
|
||||
}
|
||||
switch err {
|
||||
case win32.ERROR_ACCESS_DENIED, win32.ERROR_SHARING_VIOLATION:
|
||||
return .Permission_Denied
|
||||
|
||||
case win32.ERROR_FILE_EXISTS, win32.ERROR_ALREADY_EXISTS:
|
||||
return .Exist
|
||||
|
||||
case win32.ERROR_FILE_NOT_FOUND, win32.ERROR_PATH_NOT_FOUND:
|
||||
return .Not_Exist
|
||||
|
||||
case win32.ERROR_NO_DATA:
|
||||
return .Closed
|
||||
|
||||
case win32.ERROR_TIMEOUT, win32.WAIT_TIMEOUT:
|
||||
return .Timeout
|
||||
|
||||
case win32.ERROR_NOT_SUPPORTED:
|
||||
return .Unsupported
|
||||
|
||||
case
|
||||
win32.ERROR_BAD_ARGUMENTS,
|
||||
win32.ERROR_INVALID_PARAMETER,
|
||||
win32.ERROR_NOT_ENOUGH_MEMORY,
|
||||
win32.ERROR_INVALID_HANDLE,
|
||||
win32.ERROR_NO_MORE_FILES,
|
||||
win32.ERROR_LOCK_VIOLATION,
|
||||
win32.ERROR_HANDLE_EOF,
|
||||
win32.ERROR_BROKEN_PIPE,
|
||||
win32.ERROR_CALL_NOT_IMPLEMENTED,
|
||||
win32.ERROR_INSUFFICIENT_BUFFER,
|
||||
win32.ERROR_INVALID_NAME,
|
||||
win32.ERROR_LOCK_FAILED,
|
||||
win32.ERROR_ENVVAR_NOT_FOUND,
|
||||
win32.ERROR_OPERATION_ABORTED,
|
||||
win32.ERROR_IO_PENDING,
|
||||
win32.ERROR_NO_UNICODE_TRANSLATION:
|
||||
// fallthrough
|
||||
}
|
||||
return Platform_Error(err)
|
||||
}
|
||||
@@ -74,7 +74,7 @@ read_ptr :: proc(f: ^File, data: rawptr, len: int) -> (n: int, err: Error) {
|
||||
|
||||
|
||||
|
||||
read_entire_file :: proc(name: string, allocator := context.allocator) -> ([]byte, Error) {
|
||||
read_entire_file :: proc(name: string, allocator := context.allocator) -> (data: []byte, err: Error) {
|
||||
f, ferr := open(name)
|
||||
if ferr != nil {
|
||||
return nil, ferr
|
||||
@@ -91,15 +91,17 @@ read_entire_file :: proc(name: string, allocator := context.allocator) -> ([]byt
|
||||
|
||||
// TODO(bill): Is this correct logic?
|
||||
total: int
|
||||
data := make([]byte, size, allocator)
|
||||
data = make([]byte, size, allocator) or_return
|
||||
for {
|
||||
n, err := read(f, data[total:])
|
||||
n: int
|
||||
n, err = read(f, data[total:])
|
||||
total += n
|
||||
if err != nil {
|
||||
if err == .EOF {
|
||||
err = nil
|
||||
}
|
||||
return data[:total], err
|
||||
data = data[:total]
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package os2
|
||||
|
||||
import "core:io"
|
||||
import "core:mem"
|
||||
import "core:sync"
|
||||
import "core:runtime"
|
||||
import "core:strings"
|
||||
import "core:time"
|
||||
@@ -19,6 +20,12 @@ _file_allocator :: proc() -> runtime.Allocator {
|
||||
return heap_allocator()
|
||||
}
|
||||
|
||||
_temp_allocator :: proc() -> runtime.Allocator {
|
||||
// TODO(bill): make this not depend on the context allocator
|
||||
return context.temp_allocator
|
||||
}
|
||||
|
||||
|
||||
_File_Kind :: enum u8 {
|
||||
File,
|
||||
Console,
|
||||
@@ -30,20 +37,17 @@ _File :: struct {
|
||||
name: string,
|
||||
wname: win32.wstring,
|
||||
kind: _File_Kind,
|
||||
|
||||
allocator: runtime.Allocator,
|
||||
|
||||
rw_mutex: sync.RW_Mutex, // read write calls
|
||||
p_mutex: sync.Mutex, // pread pwrite calls
|
||||
}
|
||||
|
||||
_handle :: proc(f: ^File) -> win32.HANDLE {
|
||||
return win32.HANDLE(_fd(f))
|
||||
}
|
||||
|
||||
_get_platform_error :: proc() -> Error {
|
||||
err := i32(win32.GetLastError())
|
||||
if err != 0 {
|
||||
return Platform_Error{err}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_open_internal :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (handle: uintptr, err: Error) {
|
||||
if len(name) == 0 {
|
||||
err = .Not_Exist
|
||||
@@ -100,7 +104,7 @@ _open_internal :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (han
|
||||
case 0:
|
||||
return uintptr(h), nil
|
||||
case:
|
||||
return 0, Platform_Error{i32(e)}
|
||||
return 0, Platform_Error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,11 +127,12 @@ _new_file :: proc(handle: uintptr, name: string) -> ^File {
|
||||
if handle == INVALID_HANDLE {
|
||||
return nil
|
||||
}
|
||||
context.allocator = _file_allocator()
|
||||
f := new(File)
|
||||
f := new(File, _file_allocator())
|
||||
|
||||
f.impl.allocator = _file_allocator()
|
||||
f.impl.fd = rawptr(fd)
|
||||
f.impl.name = strings.clone(name, context.allocator)
|
||||
f.impl.wname = win32.utf8_to_wstring(name, context.allocator)
|
||||
f.impl.name = strings.clone(name, f.impl.allocator)
|
||||
f.impl.wname = win32.utf8_to_wstring(name, f.impl.allocator)
|
||||
|
||||
handle := _handle(f)
|
||||
kind := _File_Kind.File
|
||||
@@ -154,10 +159,10 @@ _destroy :: proc(f: ^File) -> Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
context.allocator = _file_allocator()
|
||||
free(f.impl.wname)
|
||||
delete(f.impl.name)
|
||||
free(f)
|
||||
a := f.impl.allocator
|
||||
free(f.impl.wname, a)
|
||||
delete(f.impl.name, a)
|
||||
free(f, a)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -185,6 +190,8 @@ _seek :: proc(f: ^File, offset: i64, whence: Seek_From) -> (ret: i64, err: Error
|
||||
return 0, .Invalid_File
|
||||
}
|
||||
|
||||
sync.guard(&f.impl.rw_mutex)
|
||||
|
||||
w: u32
|
||||
switch whence {
|
||||
case .Start: w = win32.FILE_BEGIN
|
||||
@@ -207,6 +214,7 @@ _read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// TODO(bill): should this be moved to `_File` instead?
|
||||
BUF_SIZE :: 386
|
||||
buf16: [BUF_SIZE]u16
|
||||
buf8: [4*BUF_SIZE]u8
|
||||
@@ -257,22 +265,27 @@ _read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
|
||||
total_read: int
|
||||
length := len(p)
|
||||
|
||||
to_read := min(win32.DWORD(length), MAX_RW)
|
||||
sync.shared_guard(&f.impl.rw_mutex) // multiple readers
|
||||
|
||||
e: win32.BOOL
|
||||
if f.impl.kind == .Console {
|
||||
n, err := read_console(handle, p[total_read:][:to_read])
|
||||
total_read += n
|
||||
if err != nil {
|
||||
return int(total_read), err
|
||||
if sync.guard(&f.impl.p_mutex) {
|
||||
to_read := min(win32.DWORD(length), MAX_RW)
|
||||
ok: win32.BOOL
|
||||
if f.impl.kind == .Console {
|
||||
n, err := read_console(handle, p[total_read:][:to_read])
|
||||
total_read += n
|
||||
if err != nil {
|
||||
return int(total_read), err
|
||||
}
|
||||
} else {
|
||||
ok = win32.ReadFile(handle, &p[total_read], to_read, &single_read_length, nil)
|
||||
}
|
||||
|
||||
if single_read_length > 0 && ok {
|
||||
total_read += int(single_read_length)
|
||||
} else {
|
||||
err = _get_platform_error()
|
||||
}
|
||||
} else {
|
||||
e = win32.ReadFile(handle, &p[total_read], to_read, &single_read_length, nil)
|
||||
}
|
||||
if single_read_length <= 0 || !e {
|
||||
return int(total_read), _get_platform_error()
|
||||
}
|
||||
total_read += int(single_read_length)
|
||||
|
||||
return int(total_read), nil
|
||||
}
|
||||
@@ -303,6 +316,9 @@ _read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
n = int(done)
|
||||
return
|
||||
}
|
||||
|
||||
sync.guard(&f.impl.p_mutex)
|
||||
|
||||
p, offset := p, offset
|
||||
for len(p) > 0 {
|
||||
m := pread(f, p, offset) or_return
|
||||
@@ -329,6 +345,7 @@ _write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
|
||||
|
||||
handle := _handle(f)
|
||||
|
||||
sync.guard(&f.impl.rw_mutex)
|
||||
for total_write < length {
|
||||
remaining := length - total_write
|
||||
to_write := win32.DWORD(min(i32(remaining), MAX_RW))
|
||||
@@ -369,6 +386,7 @@ _write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
return
|
||||
}
|
||||
|
||||
sync.guard(&f.impl.p_mutex)
|
||||
p, offset := p, offset
|
||||
for len(p) > 0 {
|
||||
m := pwrite(f, p, offset) or_return
|
||||
@@ -531,7 +549,7 @@ _normalize_link_path :: proc(p: []u16, allocator: runtime.Allocator) -> (str: st
|
||||
if n == 0 {
|
||||
return "", _get_platform_error()
|
||||
}
|
||||
buf := make([]u16, n+1, context.temp_allocator)
|
||||
buf := make([]u16, n+1, _temp_allocator())
|
||||
n = win32.GetFinalPathNameByHandleW(handle, raw_data(buf), u32(len(buf)), win32.VOLUME_NAME_DOS)
|
||||
if n == 0 {
|
||||
return "", _get_platform_error()
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package os2
|
||||
|
||||
import "core:runtime"
|
||||
|
||||
Path_Separator :: _Path_Separator // OS-Specific
|
||||
Path_List_Separator :: _Path_List_Separator // OS-Specific
|
||||
|
||||
@@ -21,7 +23,7 @@ remove_all :: proc(path: string) -> Error {
|
||||
|
||||
|
||||
|
||||
getwd :: proc(allocator := context.allocator) -> (dir: string, err: Error) {
|
||||
getwd :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
|
||||
return _getwd(allocator)
|
||||
}
|
||||
setwd :: proc(dir: string) -> (err: Error) {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
package os2
|
||||
|
||||
import win32 "core:sys/windows"
|
||||
import "core:runtime"
|
||||
import "core:strings"
|
||||
|
||||
_Path_Separator :: '\\'
|
||||
_Path_List_Separator :: ';'
|
||||
@@ -11,11 +13,58 @@ _is_path_separator :: proc(c: byte) -> 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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user