mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-30 11:20:08 +00:00
merge from upstream and convert to ^File types
This commit is contained in:
@@ -13,7 +13,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
|
||||
if d.cFileName[0] == '.' && d.cFileName[1] == '.' && d.cFileName[2] == 0 {
|
||||
return
|
||||
}
|
||||
path := strings.concatenate({base_path, `\`, win32.utf16_to_utf8(d.cFileName[:])})
|
||||
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)
|
||||
|
||||
@@ -22,7 +22,7 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
|
||||
}
|
||||
|
||||
if n <= u32(len(b)) {
|
||||
value = win32.utf16_to_utf8(b[:n], allocator)
|
||||
value, _ = win32.utf16_to_utf8(b[:n], allocator)
|
||||
found = true
|
||||
return
|
||||
}
|
||||
@@ -76,7 +76,7 @@ environ :: proc(allocator := context.allocator) -> []string {
|
||||
if i <= from {
|
||||
break
|
||||
}
|
||||
append(&r, win32.utf16_to_utf8(envs[from:i], allocator))
|
||||
append(&r, win32.utf16_to_utf8(envs[from:i], allocator) or_else "")
|
||||
from = i + 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,7 +365,7 @@ get_current_directory :: proc(allocator := context.allocator) -> string {
|
||||
|
||||
win32.ReleaseSRWLockExclusive(&cwd_lock)
|
||||
|
||||
return win32.utf16_to_utf8(dir_buf_wstr, allocator)
|
||||
return win32.utf16_to_utf8(dir_buf_wstr, allocator) or_else ""
|
||||
}
|
||||
|
||||
set_current_directory :: proc(path: string) -> (err: Errno) {
|
||||
|
||||
@@ -9,6 +9,10 @@ OS :: ODIN_OS
|
||||
ARCH :: ODIN_ARCH
|
||||
ENDIAN :: ODIN_ENDIAN
|
||||
|
||||
SEEK_SET :: 0
|
||||
SEEK_CUR :: 1
|
||||
SEEK_END :: 2
|
||||
|
||||
write_string :: proc(fd: Handle, str: string) -> (int, Errno) {
|
||||
return write(fd, transmute([]byte)str)
|
||||
}
|
||||
|
||||
@@ -1,33 +1,35 @@
|
||||
//+private
|
||||
package os2
|
||||
|
||||
//import "core:runtime"
|
||||
//import "core:mem"
|
||||
import win32 "core:sys/windows"
|
||||
import "core:runtime"
|
||||
|
||||
_get_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
|
||||
}
|
||||
wkey := win32.utf8_to_wstring(key)
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-getenvironmentvariablew
|
||||
buf_len := win32.GetEnvironmentVariableW(wkey, nil, 0)
|
||||
if buf_len == 0 {
|
||||
return
|
||||
}
|
||||
buf := make([dynamic]u16, buf_len, context.temp_allocator)
|
||||
n := win32.GetEnvironmentVariableW(wkey, raw_data(buf), buf_len)
|
||||
n := win32.GetEnvironmentVariableW(wkey, nil, 0)
|
||||
if n == 0 {
|
||||
if win32.GetLastError() == win32.ERROR_ENVVAR_NOT_FOUND {
|
||||
err := win32.GetLastError()
|
||||
if err == win32.ERROR_ENVVAR_NOT_FOUND {
|
||||
return "", false
|
||||
}
|
||||
value = ""
|
||||
found = true
|
||||
return
|
||||
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(buf[:n], allocator)
|
||||
value = win32.utf16_to_utf8(b[:n], allocator) or_else ""
|
||||
found = true
|
||||
return
|
||||
}
|
||||
@@ -36,21 +38,18 @@ _set_env :: proc(key, value: string) -> bool {
|
||||
k := win32.utf8_to_wstring(key)
|
||||
v := win32.utf8_to_wstring(value)
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-setenvironmentvariablew
|
||||
return bool(win32.SetEnvironmentVariableW(k, v))
|
||||
}
|
||||
|
||||
_unset_env :: proc(key: string) -> bool {
|
||||
k := win32.utf8_to_wstring(key)
|
||||
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/processenv/nf-processenv-setenvironmentvariablew
|
||||
return bool(win32.SetEnvironmentVariableW(k, nil))
|
||||
}
|
||||
|
||||
_clear_env :: proc() {
|
||||
envs := environ(context.temp_allocator)
|
||||
envs := environ(_temp_allocator())
|
||||
for env in envs {
|
||||
#no_bounds_check for j in 1..<len(env) {
|
||||
for j in 1..<len(env) {
|
||||
if env[j] == '=' {
|
||||
unset_env(env[0:j])
|
||||
break
|
||||
@@ -59,34 +58,23 @@ _clear_env :: proc() {
|
||||
}
|
||||
}
|
||||
|
||||
_environ :: proc(allocator := context.allocator) -> []string {
|
||||
envs := ([^]u16)(win32.GetEnvironmentStringsW())
|
||||
_environ :: proc(allocator: runtime.Allocator) -> []string {
|
||||
envs := win32.GetEnvironmentStringsW()
|
||||
if envs == nil {
|
||||
return nil
|
||||
}
|
||||
defer win32.FreeEnvironmentStringsW(envs)
|
||||
|
||||
length := 0
|
||||
n := 0
|
||||
count_loop: for {
|
||||
if envs[length] == 0 {
|
||||
n += 1
|
||||
if envs[length+1] == 0 {
|
||||
break count_loop
|
||||
}
|
||||
}
|
||||
|
||||
length += 1
|
||||
}
|
||||
|
||||
r := make([dynamic]string, 0, n, allocator)
|
||||
for offset, i := 0, 0; i < length && len(r) < n; i += 1 {
|
||||
c := envs[i]
|
||||
r := make([dynamic]string, 0, 50, allocator)
|
||||
for from, i, p := 0, 0, envs; true; i += 1 {
|
||||
c := ([^]u16)(p)[i]
|
||||
if c == 0 {
|
||||
wstr := envs[offset:i]
|
||||
append(&r, win32.utf16_to_utf8(wstr, allocator))
|
||||
i += 1
|
||||
offset = i
|
||||
if i <= from {
|
||||
break
|
||||
}
|
||||
w := ([^]u16)(p)[from:i]
|
||||
append(&r, win32.utf16_to_utf8(w, allocator) or_else "")
|
||||
from = i + 1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+54
-54
@@ -1,9 +1,10 @@
|
||||
package os2
|
||||
|
||||
import "core:io"
|
||||
import "core:runtime"
|
||||
|
||||
General_Error :: enum u32 {
|
||||
Invalid_Argument,
|
||||
None,
|
||||
|
||||
Permission_Denied,
|
||||
Exist,
|
||||
@@ -11,79 +12,78 @@ General_Error :: enum u32 {
|
||||
Closed,
|
||||
|
||||
Timeout,
|
||||
|
||||
Invalid_File,
|
||||
Invalid_Dir,
|
||||
Invalid_Path,
|
||||
|
||||
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))
|
||||
|
||||
Link_Error :: struct {
|
||||
op: string,
|
||||
old: string,
|
||||
new: string,
|
||||
err: Error,
|
||||
}
|
||||
|
||||
link_error_delete :: proc(lerr: Maybe(Link_Error)) {
|
||||
if err, ok := lerr.?; ok {
|
||||
context.allocator = error_allocator()
|
||||
delete(err.op)
|
||||
delete(err.old)
|
||||
delete(err.new)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
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 {
|
||||
@static general_error_strings := [General_Error]string{
|
||||
.Invalid_Argument = "invalid argument",
|
||||
.Permission_Denied = "permission denied",
|
||||
.Exist = "file already exists",
|
||||
.Not_Exist = "file does not exist",
|
||||
.Closed = "file already closed",
|
||||
.Timeout = "i/o timeout",
|
||||
}
|
||||
|
||||
@static io_error_strings := [io.Error]string{
|
||||
.None = "",
|
||||
.EOF = "eof",
|
||||
.Unexpected_EOF = "unexpected eof",
|
||||
.Short_Write = "short write",
|
||||
.Invalid_Write = "invalid write result",
|
||||
.Short_Buffer = "short buffer",
|
||||
.No_Progress = "multiple read calls return no data or error",
|
||||
.Invalid_Whence = "invalid whence",
|
||||
.Invalid_Offset = "invalid offset",
|
||||
.Invalid_Unread = "invalid unread",
|
||||
.Negative_Read = "negative read",
|
||||
.Negative_Write = "negative write",
|
||||
.Negative_Count = "negative count",
|
||||
.Buffer_Full = "buffer full",
|
||||
.Unknown = "unknown i/o error",
|
||||
.Empty = "empty i/o error",
|
||||
}
|
||||
if ferr == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
switch err in ferr {
|
||||
case General_Error: return general_error_strings[err]
|
||||
case io.Error: return io_error_strings[err]
|
||||
case Platform_Error: return _error_string(err.err)
|
||||
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_Dir: return "invalid directory"
|
||||
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"
|
||||
|
||||
@@ -130,7 +130,7 @@ EHWPOISON :: 133 /* Memory page has hardware error */
|
||||
|
||||
_get_platform_error :: proc(res: int) -> Error {
|
||||
errno := unix.get_errno(res)
|
||||
return Platform_Error{i32(errno)}
|
||||
return Platform_Error(i32(errno))
|
||||
}
|
||||
|
||||
_ok_or_error :: proc(res: int) -> 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)
|
||||
}
|
||||
+133
-101
@@ -2,10 +2,11 @@ package os2
|
||||
|
||||
import "core:io"
|
||||
import "core:time"
|
||||
import "core:runtime"
|
||||
|
||||
Handle :: distinct uintptr
|
||||
|
||||
INVALID_HANDLE :: ~Handle(0)
|
||||
File :: struct {
|
||||
impl: _File,
|
||||
}
|
||||
|
||||
Seek_From :: enum {
|
||||
Start = 0, // seek relative to the origin of the file
|
||||
@@ -20,106 +21,109 @@ File_Mode_Device :: File_Mode(1<<18)
|
||||
File_Mode_Char_Device :: File_Mode(1<<19)
|
||||
File_Mode_Sym_Link :: File_Mode(1<<20)
|
||||
|
||||
File_Mode_Perm :: File_Mode(0o777) // Unix permision bits
|
||||
|
||||
File_Flag :: enum u32 {
|
||||
Read = 0,
|
||||
Write = 1,
|
||||
Append = 2,
|
||||
Create = 3,
|
||||
Excl = 4,
|
||||
Sync = 5,
|
||||
Trunc = 6,
|
||||
Close_On_Exec = 7,
|
||||
File_Flags :: distinct bit_set[File_Flag; uint]
|
||||
File_Flag :: enum {
|
||||
Read,
|
||||
Write,
|
||||
Append,
|
||||
Create,
|
||||
Excl,
|
||||
Sync,
|
||||
Trunc,
|
||||
Sparse,
|
||||
Close_On_Exec,
|
||||
|
||||
Unbuffered_IO,
|
||||
}
|
||||
File_Flags :: distinct bit_set[File_Flag; u32]
|
||||
|
||||
O_RDONLY :: File_Flags{.Read}
|
||||
O_WRONLY :: File_Flags{.Write}
|
||||
O_RDWR :: File_Flags{.Read, .Write}
|
||||
O_APPEND :: File_Flags{.Append}
|
||||
O_CREATE :: File_Flags{.Create}
|
||||
O_EXCL :: File_Flags{.Excl}
|
||||
O_SYNC :: File_Flags{.Sync}
|
||||
O_TRUNC :: File_Flags{.Trunc}
|
||||
O_RDONLY :: File_Flags{.Read}
|
||||
O_WRONLY :: File_Flags{.Write}
|
||||
O_RDWR :: File_Flags{.Read, .Write}
|
||||
O_APPEND :: File_Flags{.Append}
|
||||
O_CREATE :: File_Flags{.Create}
|
||||
O_EXCL :: File_Flags{.Excl}
|
||||
O_SYNC :: File_Flags{.Sync}
|
||||
O_TRUNC :: File_Flags{.Trunc}
|
||||
O_SPARSE :: File_Flags{.Sparse}
|
||||
O_CLOEXEC :: File_Flags{.Close_On_Exec}
|
||||
|
||||
Std_Handle_Kind :: enum u8 {
|
||||
stdin = 0,
|
||||
stdout = 1,
|
||||
stderr = 2,
|
||||
|
||||
|
||||
stdin: ^File = nil // OS-Specific
|
||||
stdout: ^File = nil // OS-Specific
|
||||
stderr: ^File = nil // OS-Specific
|
||||
|
||||
|
||||
create :: proc(name: string) -> (^File, Error) {
|
||||
return open(name, {.Read, .Write, .Create}, File_Mode(0o777))
|
||||
}
|
||||
|
||||
stdin: Handle = std_handle(.stdin)
|
||||
stdout: Handle = std_handle(.stdout)
|
||||
stderr: Handle = std_handle(.stderr)
|
||||
|
||||
std_handle :: proc(kind: Std_Handle_Kind) -> Handle {
|
||||
return _std_handle(kind)
|
||||
}
|
||||
|
||||
create :: proc(name: string, perm: File_Mode = 0) -> (Handle, Error) {
|
||||
return open(name, {.Read, .Write, .Create}, perm)
|
||||
}
|
||||
|
||||
open :: proc(name: string, flags := File_Flags{.Read}, perm: File_Mode = 0) -> (Handle, Error) {
|
||||
flags := flags
|
||||
if .Write not_in flags {
|
||||
flags += {.Read}
|
||||
}
|
||||
open :: proc(name: string, flags := File_Flags{.Read}, perm := File_Mode(0o777)) -> (^File, Error) {
|
||||
return _open(name, flags, perm)
|
||||
}
|
||||
|
||||
close :: proc(fd: Handle) -> Error {
|
||||
return _close(fd)
|
||||
new_file :: proc(handle: uintptr, name: string) -> ^File {
|
||||
return _new_file(handle, name)
|
||||
}
|
||||
|
||||
name :: proc(fd: Handle, allocator := context.allocator) -> string {
|
||||
return _name(fd)
|
||||
}
|
||||
|
||||
seek :: proc(fd: Handle, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) {
|
||||
return _seek(fd, offset, whence)
|
||||
}
|
||||
|
||||
read :: proc(fd: Handle, p: []byte) -> (n: int, err: Error) {
|
||||
return _read(fd, p)
|
||||
}
|
||||
|
||||
read_at :: proc(fd: Handle, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
return _read_at(fd, p, offset)
|
||||
}
|
||||
|
||||
read_from :: proc(fd: Handle, r: io.Reader) -> (n: i64, err: Error) {
|
||||
return _read_from(fd, r)
|
||||
}
|
||||
|
||||
write :: proc(fd: Handle, p: []byte) -> (n: int, err: Error) {
|
||||
return _write(fd, p)
|
||||
}
|
||||
|
||||
write_at :: proc(fd: Handle, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
return _write_at(fd, p, offset)
|
||||
}
|
||||
|
||||
write_to :: proc(fd: Handle, w: io.Writer) -> (n: i64, err: Error) {
|
||||
return _write_to(fd, w)
|
||||
}
|
||||
|
||||
file_size :: proc(fd: Handle) -> (n: i64, err: Error) {
|
||||
return _file_size(fd)
|
||||
fd :: proc(f: ^File) -> uintptr {
|
||||
return _fd(f)
|
||||
}
|
||||
|
||||
|
||||
sync :: proc(fd: Handle) -> Error {
|
||||
return _sync(fd)
|
||||
close :: proc(f: ^File) -> Error {
|
||||
return _close(f)
|
||||
}
|
||||
|
||||
flush :: proc(fd: Handle) -> Error {
|
||||
return _flush(fd)
|
||||
name :: proc(f: ^File) -> string {
|
||||
return _name(f)
|
||||
}
|
||||
|
||||
truncate :: proc(fd: Handle, size: i64) -> Error {
|
||||
return _truncate(fd, size)
|
||||
seek :: proc(f: ^File, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) {
|
||||
return _seek(f, offset, whence)
|
||||
}
|
||||
|
||||
read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
|
||||
return _read(f, p)
|
||||
}
|
||||
|
||||
read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
return _read_at(f, p, offset)
|
||||
}
|
||||
|
||||
read_from :: proc(f: ^File, r: io.Reader) -> (n: i64, err: Error) {
|
||||
return _read_from(f, r)
|
||||
}
|
||||
|
||||
write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
|
||||
return _write(f, p)
|
||||
}
|
||||
|
||||
write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
return _write_at(f, p, offset)
|
||||
}
|
||||
|
||||
write_to :: proc(f: ^File, w: io.Writer) -> (n: i64, err: Error) {
|
||||
return _write_to(f, w)
|
||||
}
|
||||
|
||||
file_size :: proc(f: ^File) -> (n: i64, err: Error) {
|
||||
return _file_size(f)
|
||||
}
|
||||
|
||||
|
||||
sync :: proc(f: ^File) -> Error {
|
||||
return _sync(f)
|
||||
}
|
||||
|
||||
flush :: proc(f: ^File) -> Error {
|
||||
return _flush(f)
|
||||
}
|
||||
|
||||
truncate :: proc(f: ^File, size: i64) -> Error {
|
||||
return _truncate(f, size)
|
||||
}
|
||||
|
||||
remove :: proc(name: string) -> Error {
|
||||
@@ -139,28 +143,36 @@ symlink :: proc(old_name, new_name: string) -> Error {
|
||||
return _symlink(old_name, new_name)
|
||||
}
|
||||
|
||||
read_link :: proc(name: string) -> (string, Error) {
|
||||
return _read_link(name)
|
||||
}
|
||||
|
||||
unlink :: proc(path: string) -> Error {
|
||||
return _unlink(path)
|
||||
read_link :: proc(name: string, allocator: runtime.Allocator) -> (string, Error) {
|
||||
return _read_link(name,allocator)
|
||||
}
|
||||
|
||||
|
||||
|
||||
chdir :: proc(fd: Handle) -> Error {
|
||||
return _chdir(fd)
|
||||
chdir :: proc(name: string) -> Error {
|
||||
return _chdir(name)
|
||||
}
|
||||
|
||||
chmod :: proc(fd: Handle, mode: File_Mode) -> Error {
|
||||
return _chmod(fd, mode)
|
||||
chmod :: proc(name: string, mode: File_Mode) -> Error {
|
||||
return _chmod(name, mode)
|
||||
}
|
||||
|
||||
chown :: proc(fd: Handle, uid, gid: int) -> Error {
|
||||
return _chown(fd, uid, gid)
|
||||
chown :: proc(name: string, uid, gid: int) -> Error {
|
||||
return _chown(name, uid, gid)
|
||||
}
|
||||
|
||||
fchdir :: proc(f: ^File) -> Error {
|
||||
return _fchdir(f)
|
||||
}
|
||||
|
||||
fchmod :: proc(f: ^File, mode: File_Mode) -> Error {
|
||||
return _fchmod(f, mode)
|
||||
}
|
||||
|
||||
fchown :: proc(f: ^File, uid, gid: int) -> Error {
|
||||
return _fchown(f, uid, gid)
|
||||
}
|
||||
|
||||
|
||||
|
||||
lchown :: proc(name: string, uid, gid: int) -> Error {
|
||||
return _lchown(name, uid, gid)
|
||||
@@ -170,16 +182,36 @@ lchown :: proc(name: string, uid, gid: int) -> Error {
|
||||
chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
|
||||
return _chtimes(name, atime, mtime)
|
||||
}
|
||||
fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
|
||||
return _fchtimes(f, atime, mtime)
|
||||
}
|
||||
|
||||
exists :: proc(path: string) -> bool {
|
||||
return _exists(path)
|
||||
}
|
||||
|
||||
is_file :: proc(fd: Handle) -> bool {
|
||||
return _is_file(fd)
|
||||
is_file :: proc(path: string) -> bool {
|
||||
return _is_file(path)
|
||||
}
|
||||
|
||||
is_dir :: proc(fd: Handle) -> bool {
|
||||
return _is_dir(fd)
|
||||
is_dir :: proc(path: string) -> bool {
|
||||
return _is_dir(path)
|
||||
}
|
||||
|
||||
|
||||
copy_file :: proc(dst_path, src_path: string) -> Error {
|
||||
src := open(src_path) or_return
|
||||
defer close(src)
|
||||
|
||||
info := fstat(src, _file_allocator()) or_return
|
||||
defer file_info_delete(info, _file_allocator())
|
||||
if info.is_dir {
|
||||
return .Invalid_File
|
||||
}
|
||||
|
||||
dst := open(dst_path, {.Read, .Write, .Create, .Trunc}, info.mode & File_Mode_Perm) or_return
|
||||
defer close(dst)
|
||||
|
||||
_, err := io.copy(to_writer(dst), to_reader(src))
|
||||
return err
|
||||
}
|
||||
+141
-67
@@ -4,13 +4,10 @@ package os2
|
||||
import "core:io"
|
||||
import "core:time"
|
||||
import "core:strings"
|
||||
import "core:strconv"
|
||||
import "core:runtime"
|
||||
import "core:sys/unix"
|
||||
|
||||
|
||||
_std_handle :: proc(kind: Std_Handle_Kind) -> Handle {
|
||||
return Handle(kind)
|
||||
}
|
||||
INVALID_HANDLE :: -1
|
||||
|
||||
_O_RDONLY :: 0o0
|
||||
_O_WRONLY :: 0o1
|
||||
@@ -31,7 +28,17 @@ _AT_FDCWD :: -100
|
||||
|
||||
_CSTRING_NAME_HEAP_THRESHOLD :: 512
|
||||
|
||||
_open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (Handle, Error) {
|
||||
_File :: struct {
|
||||
name: string,
|
||||
fd: int,
|
||||
allocator: runtime.Allocator,
|
||||
}
|
||||
|
||||
_file_allocator :: proc() -> runtime.Allocator {
|
||||
return heap_allocator()
|
||||
}
|
||||
|
||||
_open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (^File, Error) {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
delete(name_cstr)
|
||||
@@ -51,63 +58,75 @@ _open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (Handle, Erro
|
||||
flags_i |= (_O_TRUNC * int(.Trunc in flags))
|
||||
flags_i |= (_O_CLOEXEC * int(.Close_On_Exec in flags))
|
||||
|
||||
handle_i := unix.sys_open(name_cstr, flags_i, int(perm))
|
||||
if handle_i < 0 {
|
||||
return INVALID_HANDLE, _get_platform_error(handle_i)
|
||||
fd := unix.sys_open(name_cstr, flags_i, int(perm))
|
||||
if fd < 0 {
|
||||
return nil, _get_platform_error(fd)
|
||||
}
|
||||
|
||||
return Handle(handle_i), nil
|
||||
return _new_file(uintptr(fd), name), nil
|
||||
}
|
||||
|
||||
_close :: proc(fd: Handle) -> Error {
|
||||
res := unix.sys_close(int(fd))
|
||||
_new_file :: proc(fd: uintptr, _: string) -> ^File {
|
||||
file := new(File, _file_allocator())
|
||||
file.impl.fd = int(fd)
|
||||
file.impl.allocator = _file_allocator()
|
||||
file.impl.name = _get_full_path(file.impl.fd, file.impl.allocator)
|
||||
return file
|
||||
}
|
||||
|
||||
_destroy :: proc(f: ^File) -> Error {
|
||||
if f == nil {
|
||||
return nil
|
||||
}
|
||||
delete(f.impl.name, f.impl.allocator)
|
||||
free(f, f.impl.allocator)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
_close :: proc(f: ^File) -> Error {
|
||||
res := unix.sys_close(f.impl.fd)
|
||||
return _ok_or_error(res)
|
||||
}
|
||||
|
||||
_name :: proc(fd: Handle, allocator := context.allocator) -> string {
|
||||
// NOTE: Not sure how portable this really is
|
||||
PROC_FD_PATH :: "/proc/self/fd/"
|
||||
|
||||
buf: [32]u8
|
||||
copy(buf[:], PROC_FD_PATH)
|
||||
|
||||
strconv.itoa(buf[len(PROC_FD_PATH):], int(fd))
|
||||
|
||||
realpath: string
|
||||
err: Error
|
||||
if realpath, err = _read_link_cstr(cstring(&buf[0]), allocator); err != nil || realpath[0] != '/' {
|
||||
return ""
|
||||
_fd :: proc(f: ^File) -> uintptr {
|
||||
if f == nil {
|
||||
return ~uintptr(0)
|
||||
}
|
||||
return realpath
|
||||
return uintptr(f.impl.fd)
|
||||
}
|
||||
|
||||
_seek :: proc(fd: Handle, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) {
|
||||
res := unix.sys_lseek(int(fd), offset, int(whence))
|
||||
_name :: proc(f: ^File) -> string {
|
||||
return f.impl.name if f != nil else ""
|
||||
}
|
||||
|
||||
_seek :: proc(f: ^File, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) {
|
||||
res := unix.sys_lseek(f.impl.fd, offset, int(whence))
|
||||
if res < 0 {
|
||||
return -1, _get_platform_error(int(res))
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
_read :: proc(fd: Handle, p: []byte) -> (n: int, err: Error) {
|
||||
_read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
|
||||
if len(p) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
n = unix.sys_read(int(fd), &p[0], len(p))
|
||||
n = unix.sys_read(f.impl.fd, &p[0], len(p))
|
||||
if n < 0 {
|
||||
return -1, _get_platform_error(int(unix.get_errno(n)))
|
||||
return -1, _get_platform_error(n)
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
_read_at :: proc(fd: Handle, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
_read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
if offset < 0 {
|
||||
return 0, .Invalid_Offset
|
||||
}
|
||||
|
||||
b, offset := p, offset
|
||||
for len(b) > 0 {
|
||||
m := unix.sys_pread(int(fd), &b[0], len(b), offset)
|
||||
m := unix.sys_pread(f.impl.fd, &b[0], len(b), offset)
|
||||
if m < 0 {
|
||||
return -1, _get_platform_error(m)
|
||||
}
|
||||
@@ -118,30 +137,30 @@ _read_at :: proc(fd: Handle, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
return
|
||||
}
|
||||
|
||||
_read_from :: proc(fd: Handle, r: io.Reader) -> (n: i64, err: Error) {
|
||||
_read_from :: proc(f: ^File, r: io.Reader) -> (n: i64, err: Error) {
|
||||
//TODO
|
||||
return
|
||||
}
|
||||
|
||||
_write :: proc(fd: Handle, p: []byte) -> (n: int, err: Error) {
|
||||
_write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
|
||||
if len(p) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
n = unix.sys_write(int(fd), &p[0], uint(len(p)))
|
||||
n = unix.sys_write(f.impl.fd, &p[0], uint(len(p)))
|
||||
if n < 0 {
|
||||
return -1, _get_platform_error(n)
|
||||
}
|
||||
return int(n), nil
|
||||
}
|
||||
|
||||
_write_at :: proc(fd: Handle, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
_write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
if offset < 0 {
|
||||
return 0, .Invalid_Offset
|
||||
}
|
||||
|
||||
b, offset := p, offset
|
||||
for len(b) > 0 {
|
||||
m := unix.sys_pwrite(int(fd), &b[0], len(b), offset)
|
||||
m := unix.sys_pwrite(f.impl.fd, &b[0], len(b), offset)
|
||||
if m < 0 {
|
||||
return -1, _get_platform_error(m)
|
||||
}
|
||||
@@ -152,30 +171,30 @@ _write_at :: proc(fd: Handle, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
return
|
||||
}
|
||||
|
||||
_write_to :: proc(fd: Handle, w: io.Writer) -> (n: i64, err: Error) {
|
||||
_write_to :: proc(f: ^File, w: io.Writer) -> (n: i64, err: Error) {
|
||||
//TODO
|
||||
return
|
||||
}
|
||||
|
||||
_file_size :: proc(fd: Handle) -> (n: i64, err: Error) {
|
||||
s: OS_Stat = ---
|
||||
res := unix.sys_fstat(int(fd), &s)
|
||||
_file_size :: proc(f: ^File) -> (n: i64, err: Error) {
|
||||
s: _Stat = ---
|
||||
res := unix.sys_fstat(f.impl.fd, &s)
|
||||
if res < 0 {
|
||||
return -1, _get_platform_error(res)
|
||||
}
|
||||
return s.size, nil
|
||||
}
|
||||
|
||||
_sync :: proc(fd: Handle) -> Error {
|
||||
return _ok_or_error(unix.sys_fsync(int(fd)))
|
||||
_sync :: proc(f: ^File) -> Error {
|
||||
return _ok_or_error(unix.sys_fsync(f.impl.fd))
|
||||
}
|
||||
|
||||
_flush :: proc(fd: Handle) -> Error {
|
||||
return _ok_or_error(unix.sys_fsync(int(fd)))
|
||||
_flush :: proc(f: ^File) -> Error {
|
||||
return _ok_or_error(unix.sys_fsync(f.impl.fd))
|
||||
}
|
||||
|
||||
_truncate :: proc(fd: Handle, size: i64) -> Error {
|
||||
return _ok_or_error(unix.sys_ftruncate(int(fd), size))
|
||||
_truncate :: proc(f: ^File, size: i64) -> Error {
|
||||
return _ok_or_error(unix.sys_ftruncate(f.impl.fd, size))
|
||||
}
|
||||
|
||||
_remove :: proc(name: string) -> Error {
|
||||
@@ -184,13 +203,13 @@ _remove :: proc(name: string) -> Error {
|
||||
delete(name_cstr)
|
||||
}
|
||||
|
||||
handle_i := unix.sys_open(name_cstr, int(File_Flags.Read))
|
||||
if handle_i < 0 {
|
||||
return _get_platform_error(handle_i)
|
||||
fd := unix.sys_open(name_cstr, int(File_Flags.Read))
|
||||
if fd < 0 {
|
||||
return _get_platform_error(fd)
|
||||
}
|
||||
defer unix.sys_close(handle_i)
|
||||
defer unix.sys_close(fd)
|
||||
|
||||
if _is_dir(Handle(handle_i)) {
|
||||
if _is_dir_fd(fd) {
|
||||
return _ok_or_error(unix.sys_rmdir(name_cstr))
|
||||
}
|
||||
return _ok_or_error(unix.sys_unlink(name_cstr))
|
||||
@@ -242,7 +261,7 @@ _read_link_cstr :: proc(name_cstr: cstring, allocator := context.allocator) -> (
|
||||
rc := unix.sys_readlink(name_cstr, &(buf[0]), bufsz)
|
||||
if rc < 0 {
|
||||
delete(buf)
|
||||
return "", _get_platform_error(int(unix.get_errno(rc)))
|
||||
return "", _get_platform_error(rc)
|
||||
} else if rc == int(bufsz) {
|
||||
bufsz *= 2
|
||||
delete(buf)
|
||||
@@ -269,18 +288,40 @@ _unlink :: proc(name: string) -> Error {
|
||||
return _ok_or_error(unix.sys_unlink(name_cstr))
|
||||
}
|
||||
|
||||
_chdir :: proc(fd: Handle) -> Error {
|
||||
return _ok_or_error(unix.sys_fchdir(int(fd)))
|
||||
_chdir :: proc(name: string) -> Error {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
delete(name_cstr)
|
||||
}
|
||||
return _ok_or_error(unix.sys_chdir(name_cstr))
|
||||
}
|
||||
|
||||
_chmod :: proc(fd: Handle, mode: File_Mode) -> Error {
|
||||
return _ok_or_error(unix.sys_fchmod(int(fd), int(mode)))
|
||||
_fchdir :: proc(f: ^File) -> Error {
|
||||
return _ok_or_error(unix.sys_fchdir(f.impl.fd))
|
||||
}
|
||||
|
||||
_chown :: proc(fd: Handle, uid, gid: int) -> Error {
|
||||
return _ok_or_error(unix.sys_fchown(int(fd), uid, gid))
|
||||
_chmod :: proc(name: string, mode: File_Mode) -> Error {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
delete(name_cstr)
|
||||
}
|
||||
return _ok_or_error(unix.sys_chmod(name_cstr, int(mode)))
|
||||
}
|
||||
|
||||
_fchmod :: proc(f: ^File, mode: File_Mode) -> Error {
|
||||
return _ok_or_error(unix.sys_fchmod(f.impl.fd, int(mode)))
|
||||
}
|
||||
|
||||
// NOTE: will throw error without super user priviledges
|
||||
_chown :: proc(name: string, uid, gid: int) -> Error {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
delete(name_cstr)
|
||||
}
|
||||
return _ok_or_error(unix.sys_chown(name_cstr, uid, gid))
|
||||
}
|
||||
|
||||
// NOTE: will throw error without super user priviledges
|
||||
_lchown :: proc(name: string, uid, gid: int) -> Error {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
@@ -289,6 +330,11 @@ _lchown :: proc(name: string, uid, gid: int) -> Error {
|
||||
return _ok_or_error(unix.sys_lchown(name_cstr, uid, gid))
|
||||
}
|
||||
|
||||
// NOTE: will throw error without super user priviledges
|
||||
_fchown :: proc(f: ^File, uid, gid: int) -> Error {
|
||||
return _ok_or_error(unix.sys_fchown(f.impl.fd, uid, gid))
|
||||
}
|
||||
|
||||
_chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
@@ -301,6 +347,14 @@ _chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
|
||||
return _ok_or_error(unix.sys_utimensat(_AT_FDCWD, name_cstr, ×, 0))
|
||||
}
|
||||
|
||||
_fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
|
||||
times := [2]Unix_File_Time {
|
||||
{ atime._nsec, 0 },
|
||||
{ mtime._nsec, 0 },
|
||||
}
|
||||
return _ok_or_error(unix.sys_utimensat(f.impl.fd, nil, ×, 0))
|
||||
}
|
||||
|
||||
_exists :: proc(name: string) -> bool {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
@@ -309,18 +363,38 @@ _exists :: proc(name: string) -> bool {
|
||||
return unix.sys_access(name_cstr, F_OK) == 0
|
||||
}
|
||||
|
||||
_is_file :: proc(fd: Handle) -> bool {
|
||||
s: OS_Stat
|
||||
res := unix.sys_fstat(int(fd), &s)
|
||||
_is_file :: proc(name: string) -> bool {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
delete(name_cstr)
|
||||
}
|
||||
s: _Stat
|
||||
res := unix.sys_stat(name_cstr, &s)
|
||||
return S_ISREG(s.mode)
|
||||
}
|
||||
|
||||
_is_file_fd :: proc(fd: int) -> bool {
|
||||
s: _Stat
|
||||
res := unix.sys_fstat(fd, &s)
|
||||
if res < 0 { // error
|
||||
return false
|
||||
}
|
||||
return S_ISREG(s.mode)
|
||||
}
|
||||
|
||||
_is_dir :: proc(fd: Handle) -> bool {
|
||||
s: OS_Stat
|
||||
res := unix.sys_fstat(int(fd), &s)
|
||||
_is_dir :: proc(name: string) -> bool {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
delete(name_cstr)
|
||||
}
|
||||
s: _Stat
|
||||
res := unix.sys_stat(name_cstr, &s)
|
||||
return S_ISDIR(s.mode)
|
||||
}
|
||||
|
||||
_is_dir_fd :: proc(fd: int) -> bool {
|
||||
s: _Stat
|
||||
res := unix.sys_fstat(fd, &s)
|
||||
if res < 0 { // error
|
||||
return false
|
||||
}
|
||||
@@ -330,7 +404,7 @@ _is_dir :: proc(fd: Handle) -> bool {
|
||||
// Ideally we want to use the temp_allocator. PATH_MAX on Linux is commonly
|
||||
// defined as 512, however, it is well known that paths can exceed that limit.
|
||||
// So, in theory you could have a path larger than the entire temp_allocator's
|
||||
// buffer. Therefor any large paths will use context.allocator.
|
||||
// buffer. Therefor, any large paths will use context.allocator.
|
||||
_name_to_cstring :: proc(name: string) -> (cname: cstring, allocated: bool) {
|
||||
if len(name) > _CSTRING_NAME_HEAP_THRESHOLD {
|
||||
cname = strings.clone_to_cstring(name)
|
||||
|
||||
@@ -2,12 +2,20 @@ package os2
|
||||
|
||||
import "core:io"
|
||||
|
||||
file_to_stream :: proc(fd: Handle) -> (s: io.Stream) {
|
||||
s.stream_data = rawptr(uintptr(fd))
|
||||
to_stream :: proc(f: ^File) -> (s: io.Stream) {
|
||||
s.stream_data = f
|
||||
s.stream_vtable = _file_stream_vtable
|
||||
return
|
||||
}
|
||||
|
||||
to_writer :: proc(f: ^File) -> (s: io.Writer) {
|
||||
return {to_stream(f)}
|
||||
}
|
||||
to_reader :: proc(f: ^File) -> (s: io.Reader) {
|
||||
return {to_stream(f)}
|
||||
}
|
||||
|
||||
|
||||
@(private)
|
||||
error_to_io_error :: proc(ferr: Error) -> io.Error {
|
||||
if ferr == nil {
|
||||
@@ -20,66 +28,66 @@ error_to_io_error :: proc(ferr: Error) -> io.Error {
|
||||
@(private)
|
||||
_file_stream_vtable := &io.Stream_VTable{
|
||||
impl_read = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||
fd := Handle(uintptr(s.stream_data))
|
||||
f := (^File)(s.stream_data)
|
||||
ferr: Error
|
||||
n, ferr = read(fd, p)
|
||||
n, ferr = read(f, p)
|
||||
err = error_to_io_error(ferr)
|
||||
return
|
||||
},
|
||||
impl_read_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
|
||||
fd := Handle(uintptr(s.stream_data))
|
||||
f := (^File)(s.stream_data)
|
||||
ferr: Error
|
||||
n, ferr = read_at(fd, p, offset)
|
||||
n, ferr = read_at(f, p, offset)
|
||||
err = error_to_io_error(ferr)
|
||||
return
|
||||
},
|
||||
impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) {
|
||||
fd := Handle(uintptr(s.stream_data))
|
||||
f := (^File)(s.stream_data)
|
||||
ferr: Error
|
||||
n, ferr = write_to(fd, w)
|
||||
n, ferr = write_to(f, w)
|
||||
err = error_to_io_error(ferr)
|
||||
return
|
||||
},
|
||||
impl_write = proc(s: io.Stream, p: []byte) -> (n: int, err: io.Error) {
|
||||
fd := Handle(uintptr(s.stream_data))
|
||||
f := (^File)(s.stream_data)
|
||||
ferr: Error
|
||||
n, ferr = write(fd, p)
|
||||
n, ferr = write(f, p)
|
||||
err = error_to_io_error(ferr)
|
||||
return
|
||||
},
|
||||
impl_write_at = proc(s: io.Stream, p: []byte, offset: i64) -> (n: int, err: io.Error) {
|
||||
fd := Handle(uintptr(s.stream_data))
|
||||
f := (^File)(s.stream_data)
|
||||
ferr: Error
|
||||
n, ferr = write_at(fd, p, offset)
|
||||
n, ferr = write_at(f, p, offset)
|
||||
err = error_to_io_error(ferr)
|
||||
return
|
||||
},
|
||||
impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) {
|
||||
fd := Handle(uintptr(s.stream_data))
|
||||
f := (^File)(s.stream_data)
|
||||
ferr: Error
|
||||
n, ferr = read_from(fd, r)
|
||||
n, ferr = read_from(f, r)
|
||||
err = error_to_io_error(ferr)
|
||||
return
|
||||
},
|
||||
impl_seek = proc(s: io.Stream, offset: i64, whence: io.Seek_From) -> (i64, io.Error) {
|
||||
fd := Handle(uintptr(s.stream_data))
|
||||
n, ferr := seek(fd, offset, Seek_From(whence))
|
||||
f := (^File)(s.stream_data)
|
||||
n, ferr := seek(f, offset, Seek_From(whence))
|
||||
err := error_to_io_error(ferr)
|
||||
return n, err
|
||||
},
|
||||
impl_size = proc(s: io.Stream) -> i64 {
|
||||
fd := Handle(uintptr(s.stream_data))
|
||||
sz, _ := file_size(fd)
|
||||
f := (^File)(s.stream_data)
|
||||
sz, _ := file_size(f)
|
||||
return sz
|
||||
},
|
||||
impl_flush = proc(s: io.Stream) -> io.Error {
|
||||
fd := Handle(uintptr(s.stream_data))
|
||||
ferr := flush(fd)
|
||||
f := (^File)(s.stream_data)
|
||||
ferr := flush(f)
|
||||
return error_to_io_error(ferr)
|
||||
},
|
||||
impl_close = proc(s: io.Stream) -> io.Error {
|
||||
fd := Handle(uintptr(s.stream_data))
|
||||
ferr := close(fd)
|
||||
f := (^File)(s.stream_data)
|
||||
ferr := close(f)
|
||||
return error_to_io_error(ferr)
|
||||
},
|
||||
}
|
||||
|
||||
+61
-83
@@ -4,25 +4,25 @@ import "core:mem"
|
||||
import "core:strconv"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
write_string :: proc(fd: Handle, s: string) -> (n: int, err: Error) {
|
||||
return write(fd, transmute([]byte)s)
|
||||
write_string :: proc(f: ^File, s: string) -> (n: int, err: Error) {
|
||||
return write(f, transmute([]byte)s)
|
||||
}
|
||||
|
||||
write_byte :: proc(fd: Handle, b: byte) -> (n: int, err: Error) {
|
||||
return write(fd, []byte{b})
|
||||
write_byte :: proc(f: ^File, b: byte) -> (n: int, err: Error) {
|
||||
return write(f, []byte{b})
|
||||
}
|
||||
|
||||
write_rune :: proc(fd: Handle, r: rune) -> (n: int, err: Error) {
|
||||
write_rune :: proc(f: ^File, r: rune) -> (n: int, err: Error) {
|
||||
if r < utf8.RUNE_SELF {
|
||||
return write_byte(fd, byte(r))
|
||||
return write_byte(f, byte(r))
|
||||
}
|
||||
|
||||
b: [4]byte
|
||||
b, n = utf8.encode_rune(r)
|
||||
return write(fd, b[:n])
|
||||
return write(f, b[:n])
|
||||
}
|
||||
|
||||
write_encoded_rune :: proc(fd: Handle, r: rune) -> (n: int, err: Error) {
|
||||
write_encoded_rune :: proc(f: ^File, r: rune) -> (n: int, err: Error) {
|
||||
wrap :: proc(m: int, merr: Error, n: ^int, err: ^Error) -> bool {
|
||||
n^ += m
|
||||
if merr != nil {
|
||||
@@ -32,102 +32,78 @@ write_encoded_rune :: proc(fd: Handle, r: rune) -> (n: int, err: Error) {
|
||||
return false
|
||||
}
|
||||
|
||||
if wrap(write_byte(fd, '\''), &n, &err) { return }
|
||||
if wrap(write_byte(f, '\''), &n, &err) { return }
|
||||
|
||||
switch r {
|
||||
case '\a': if wrap(write_string(fd, "\\a"), &n, &err) { return }
|
||||
case '\b': if wrap(write_string(fd, "\\b"), &n, &err) { return }
|
||||
case '\e': if wrap(write_string(fd, "\\e"), &n, &err) { return }
|
||||
case '\f': if wrap(write_string(fd, "\\f"), &n, &err) { return }
|
||||
case '\n': if wrap(write_string(fd, "\\n"), &n, &err) { return }
|
||||
case '\r': if wrap(write_string(fd, "\\r"), &n, &err) { return }
|
||||
case '\t': if wrap(write_string(fd, "\\t"), &n, &err) { return }
|
||||
case '\v': if wrap(write_string(fd, "\\v"), &n, &err) { return }
|
||||
case '\a': if wrap(write_string(f, "\\a"), &n, &err) { return }
|
||||
case '\b': if wrap(write_string(f, "\\b"), &n, &err) { return }
|
||||
case '\e': if wrap(write_string(f, "\\e"), &n, &err) { return }
|
||||
case '\f': if wrap(write_string(f, "\\f"), &n, &err) { return }
|
||||
case '\n': if wrap(write_string(f, "\\n"), &n, &err) { return }
|
||||
case '\r': if wrap(write_string(f, "\\r"), &n, &err) { return }
|
||||
case '\t': if wrap(write_string(f, "\\t"), &n, &err) { return }
|
||||
case '\v': if wrap(write_string(f, "\\v"), &n, &err) { return }
|
||||
case:
|
||||
if r < 32 {
|
||||
if wrap(write_string(fd, "\\x"), &n, &err) { return }
|
||||
if wrap(write_string(f, "\\x"), &n, &err) { return }
|
||||
b: [2]byte
|
||||
s := strconv.append_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil)
|
||||
switch len(s) {
|
||||
case 0: if wrap(write_string(fd, "00"), &n, &err) { return }
|
||||
case 1: if wrap(write_rune(fd, '0'), &n, &err) { return }
|
||||
case 2: if wrap(write_string(fd, s), &n, &err) { return }
|
||||
case 0: if wrap(write_string(f, "00"), &n, &err) { return }
|
||||
case 1: if wrap(write_rune(f, '0'), &n, &err) { return }
|
||||
case 2: if wrap(write_string(f, s), &n, &err) { return }
|
||||
}
|
||||
} else {
|
||||
if wrap(write_rune(fd, r), &n, &err) { return }
|
||||
if wrap(write_rune(f, r), &n, &err) { return }
|
||||
}
|
||||
}
|
||||
_ = wrap(write_byte(fd, '\''), &n, &err)
|
||||
_ = wrap(write_byte(f, '\''), &n, &err)
|
||||
return
|
||||
}
|
||||
|
||||
write_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (n: int, err: Error) {
|
||||
|
||||
write_ptr :: proc(f: ^File, data: rawptr, len: int) -> (n: int, err: Error) {
|
||||
s := transmute([]byte)mem.Raw_Slice{data, len}
|
||||
return write(fd, s)
|
||||
return write(f, s)
|
||||
}
|
||||
|
||||
read_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (n: int, err: Error) {
|
||||
read_ptr :: proc(f: ^File, data: rawptr, len: int) -> (n: int, err: Error) {
|
||||
s := transmute([]byte)mem.Raw_Slice{data, len}
|
||||
return read(fd, s)
|
||||
return read(f, s)
|
||||
}
|
||||
|
||||
|
||||
read_at_least :: proc(fd: Handle, buf: []byte, min: int) -> (n: int, err: Error) {
|
||||
if len(buf) < min {
|
||||
return 0, .Short_Buffer
|
||||
|
||||
read_entire_file :: proc(name: string, allocator := context.allocator) -> (data: []byte, err: Error) {
|
||||
f, ferr := open(name)
|
||||
if ferr != nil {
|
||||
return nil, ferr
|
||||
}
|
||||
for n < min && err == nil {
|
||||
nn: int
|
||||
nn, err = read(fd, buf[n:])
|
||||
n += nn
|
||||
defer close(f)
|
||||
|
||||
size: int
|
||||
if size64, err := file_size(f); err == nil {
|
||||
if i64(int(size64)) != size64 {
|
||||
size = int(size64)
|
||||
}
|
||||
}
|
||||
if n >= min {
|
||||
err = nil
|
||||
size += 1 // for EOF
|
||||
|
||||
// TODO(bill): Is this correct logic?
|
||||
total: int
|
||||
data = make([]byte, size, allocator) or_return
|
||||
for {
|
||||
n: int
|
||||
n, err = read(f, data[total:])
|
||||
total += n
|
||||
if err != nil {
|
||||
if err == .EOF {
|
||||
err = nil
|
||||
}
|
||||
data = data[:total]
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
read_full :: proc(fd: Handle, buf: []byte) -> (n: int, err: Error) {
|
||||
return read_at_least(fd, buf, len(buf))
|
||||
}
|
||||
|
||||
file_size_from_path :: proc(path: string) -> (length: i64, err: Error) {
|
||||
fd := open(path, O_RDONLY, 0) or_return
|
||||
defer close(fd)
|
||||
return file_size(fd)
|
||||
}
|
||||
|
||||
read_entire_file :: proc{
|
||||
read_entire_file_from_path,
|
||||
read_entire_file_from_handle,
|
||||
}
|
||||
|
||||
read_entire_file_from_path :: proc(name: string, allocator := context.allocator) -> (data: []byte, err: Error) {
|
||||
fd := open(name, {.Read}) or_return
|
||||
defer close(fd)
|
||||
return read_entire_file_from_handle(fd, allocator)
|
||||
}
|
||||
|
||||
read_entire_file_from_handle :: proc(fd: Handle, allocator := context.allocator) -> (data: []byte, err: Error) {
|
||||
length := file_size(fd) or_return
|
||||
if length <= 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if i64(int(length)) != length {
|
||||
return nil, .Short_Buffer
|
||||
}
|
||||
|
||||
data = make([]byte, int(length), allocator)
|
||||
if data == nil {
|
||||
return nil, .Short_Buffer
|
||||
}
|
||||
defer if err != nil {
|
||||
delete(data, allocator)
|
||||
}
|
||||
|
||||
bytes_read := read_full(fd, data) or_return
|
||||
return data[:bytes_read], nil
|
||||
}
|
||||
|
||||
write_entire_file :: proc(name: string, data: []byte, perm: File_Mode, truncate := true) -> Error {
|
||||
@@ -135,9 +111,11 @@ write_entire_file :: proc(name: string, data: []byte, perm: File_Mode, truncate
|
||||
if truncate {
|
||||
flags |= O_TRUNC
|
||||
}
|
||||
f := open(name, flags, perm) or_return
|
||||
|
||||
_, err := write(f, data)
|
||||
f, err := open(name, flags, perm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = write(f, data)
|
||||
if cerr := close(f); cerr != nil && err == nil {
|
||||
err = cerr
|
||||
}
|
||||
|
||||
+550
-242
@@ -2,323 +2,455 @@
|
||||
package os2
|
||||
|
||||
import "core:io"
|
||||
import "core:mem"
|
||||
import "core:sync"
|
||||
import "core:runtime"
|
||||
import "core:strings"
|
||||
import "core:time"
|
||||
import "core:unicode/utf16"
|
||||
import win32 "core:sys/windows"
|
||||
|
||||
_get_platform_error :: proc() -> Error {
|
||||
// TODO(bill): map some of these errors correctly
|
||||
err := win32.GetLastError()
|
||||
if err == 0 {
|
||||
return nil
|
||||
}
|
||||
return Platform_Error{i32(err)}
|
||||
INVALID_HANDLE :: ~uintptr(0)
|
||||
|
||||
S_IWRITE :: 0o200
|
||||
_ERROR_BAD_NETPATH :: 53
|
||||
MAX_RW :: 1<<30
|
||||
|
||||
_file_allocator :: proc() -> runtime.Allocator {
|
||||
return heap_allocator()
|
||||
}
|
||||
|
||||
_ok_or_error :: proc(ok: win32.BOOL) -> Error {
|
||||
return nil if ok else _get_platform_error()
|
||||
_temp_allocator :: proc() -> runtime.Allocator {
|
||||
// TODO(bill): make this not depend on the context allocator
|
||||
return context.temp_allocator
|
||||
}
|
||||
|
||||
_std_handle :: proc(kind: Std_Handle_Kind) -> Handle {
|
||||
get_handle :: proc(h: win32.DWORD) -> Handle {
|
||||
fd := win32.GetStdHandle(h)
|
||||
when size_of(uintptr) == 8 {
|
||||
win32.SetHandleInformation(fd, win32.HANDLE_FLAG_INHERIT, 0)
|
||||
}
|
||||
return Handle(fd)
|
||||
}
|
||||
|
||||
switch kind {
|
||||
case .stdin: return get_handle(win32.STD_INPUT_HANDLE)
|
||||
case .stdout: return get_handle(win32.STD_OUTPUT_HANDLE)
|
||||
case .stderr: return get_handle(win32.STD_ERROR_HANDLE)
|
||||
}
|
||||
unreachable()
|
||||
_File_Kind :: enum u8 {
|
||||
File,
|
||||
Console,
|
||||
Pipe,
|
||||
}
|
||||
|
||||
_open :: proc(path: string, flags: File_Flags, perm: File_Mode) -> (handle: Handle, err: Error) {
|
||||
handle = INVALID_HANDLE
|
||||
if len(path) == 0 {
|
||||
_File :: struct {
|
||||
fd: rawptr,
|
||||
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))
|
||||
}
|
||||
|
||||
_open_internal :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (handle: uintptr, err: Error) {
|
||||
if len(name) == 0 {
|
||||
err = .Not_Exist
|
||||
return
|
||||
}
|
||||
|
||||
path := _fix_long_path(name)
|
||||
access: u32
|
||||
switch flags & O_RDONLY|O_WRONLY|O_RDWR {
|
||||
case O_RDONLY: access = win32.FILE_GENERIC_READ
|
||||
case O_WRONLY: access = win32.FILE_GENERIC_WRITE
|
||||
case O_RDWR: access = win32.FILE_GENERIC_READ | win32.FILE_GENERIC_WRITE
|
||||
switch flags & {.Read, .Write} {
|
||||
case {.Read}: access = win32.FILE_GENERIC_READ
|
||||
case {.Write}: access = win32.FILE_GENERIC_WRITE
|
||||
case {.Read, .Write}: access = win32.FILE_GENERIC_READ | win32.FILE_GENERIC_WRITE
|
||||
}
|
||||
|
||||
if .Append in flags {
|
||||
access &~= win32.FILE_GENERIC_WRITE
|
||||
access |= win32.FILE_APPEND_DATA
|
||||
}
|
||||
if .Create in flags {
|
||||
access |= win32.FILE_GENERIC_WRITE
|
||||
}
|
||||
|
||||
share_mode := win32.FILE_SHARE_READ|win32.FILE_SHARE_WRITE
|
||||
sa: ^win32.SECURITY_ATTRIBUTES = nil
|
||||
sa_inherit := win32.SECURITY_ATTRIBUTES{nLength = size_of(win32.SECURITY_ATTRIBUTES), bInheritHandle = true}
|
||||
if .Close_On_Exec in flags {
|
||||
sa = &sa_inherit
|
||||
if .Append in flags {
|
||||
access &~= win32.FILE_GENERIC_WRITE
|
||||
access |= win32.FILE_APPEND_DATA
|
||||
}
|
||||
share_mode := u32(win32.FILE_SHARE_READ | win32.FILE_SHARE_WRITE)
|
||||
sa: ^win32.SECURITY_ATTRIBUTES
|
||||
if .Close_On_Exec not_in flags {
|
||||
sa = &win32.SECURITY_ATTRIBUTES{}
|
||||
sa.nLength = size_of(win32.SECURITY_ATTRIBUTES)
|
||||
sa.bInheritHandle = true
|
||||
}
|
||||
|
||||
create_mode: u32
|
||||
create_mode: u32 = win32.OPEN_EXISTING
|
||||
switch {
|
||||
case flags&(O_CREATE|O_EXCL) == (O_CREATE | O_EXCL):
|
||||
case flags & {.Create, .Excl} == {.Create, .Excl}:
|
||||
create_mode = win32.CREATE_NEW
|
||||
case flags&(O_CREATE|O_TRUNC) == (O_CREATE | O_TRUNC):
|
||||
case flags & {.Create, .Trunc} == {.Create, .Trunc}:
|
||||
create_mode = win32.CREATE_ALWAYS
|
||||
case flags&O_CREATE == O_CREATE:
|
||||
case flags & {.Create} == {.Create}:
|
||||
create_mode = win32.OPEN_ALWAYS
|
||||
case flags&O_TRUNC == O_TRUNC:
|
||||
case flags & {.Trunc} == {.Trunc}:
|
||||
create_mode = win32.TRUNCATE_EXISTING
|
||||
case:
|
||||
create_mode = win32.OPEN_EXISTING
|
||||
}
|
||||
wide_path := win32.utf8_to_wstring(path)
|
||||
handle = Handle(win32.CreateFileW(wide_path, access, share_mode, sa, create_mode, win32.FILE_ATTRIBUTE_NORMAL|win32.FILE_FLAG_BACKUP_SEMANTICS, nil))
|
||||
if handle == INVALID_HANDLE {
|
||||
err = _get_platform_error()
|
||||
|
||||
attrs: u32 = win32.FILE_ATTRIBUTE_NORMAL
|
||||
if perm & S_IWRITE == 0 {
|
||||
attrs = win32.FILE_ATTRIBUTE_READONLY
|
||||
if create_mode == win32.CREATE_ALWAYS {
|
||||
// NOTE(bill): Open has just asked to create a file in read-only mode.
|
||||
// If the file already exists, to make it akin to a *nix open call,
|
||||
// the call preserves the existing permissions.
|
||||
h := win32.CreateFileW(path, access, share_mode, sa, win32.TRUNCATE_EXISTING, win32.FILE_ATTRIBUTE_NORMAL, nil)
|
||||
if h == win32.INVALID_HANDLE {
|
||||
switch e := win32.GetLastError(); e {
|
||||
case win32.ERROR_FILE_NOT_FOUND, _ERROR_BAD_NETPATH, win32.ERROR_PATH_NOT_FOUND:
|
||||
// file does not exist, create the file
|
||||
case 0:
|
||||
return uintptr(h), nil
|
||||
case:
|
||||
return 0, Platform_Error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
h := win32.CreateFileW(path, access, share_mode, sa, create_mode, attrs, nil)
|
||||
if h == win32.INVALID_HANDLE {
|
||||
return 0, _get_platform_error()
|
||||
}
|
||||
return uintptr(h), nil
|
||||
}
|
||||
|
||||
_close :: proc(fd: Handle) -> Error {
|
||||
if fd == 0 {
|
||||
return .Invalid_Argument
|
||||
|
||||
_open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (f: ^File, err: Error) {
|
||||
flags := flags if flags != nil else {.Read}
|
||||
handle := _open_internal(name, flags + {.Close_On_Exec}, perm) or_return
|
||||
return _new_file(handle, name), nil
|
||||
}
|
||||
|
||||
_new_file :: proc(handle: uintptr, name: string) -> ^File {
|
||||
if handle == INVALID_HANDLE {
|
||||
return nil
|
||||
}
|
||||
hnd := win32.HANDLE(fd)
|
||||
f := new(File, _file_allocator())
|
||||
|
||||
file_info: win32.BY_HANDLE_FILE_INFORMATION
|
||||
_ok_or_error(win32.GetFileInformationByHandle(hnd, &file_info)) or_return
|
||||
f.impl.allocator = _file_allocator()
|
||||
f.impl.fd = rawptr(fd)
|
||||
f.impl.name = strings.clone(name, f.impl.allocator)
|
||||
f.impl.wname = win32.utf8_to_wstring(name, f.impl.allocator)
|
||||
|
||||
if file_info.dwFileAttributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||
handle := _handle(f)
|
||||
kind := _File_Kind.File
|
||||
if m: u32; win32.GetConsoleMode(handle, &m) {
|
||||
kind = .Console
|
||||
}
|
||||
if win32.GetFileType(handle) == win32.FILE_TYPE_PIPE {
|
||||
kind = .Pipe
|
||||
}
|
||||
f.impl.kind = kind
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
_fd :: proc(f: ^File) -> uintptr {
|
||||
if f == nil {
|
||||
return INVALID_HANDLE
|
||||
}
|
||||
return uintptr(f.impl.fd)
|
||||
}
|
||||
|
||||
_destroy :: proc(f: ^File) -> Error {
|
||||
if f == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return _ok_or_error(win32.CloseHandle(hnd))
|
||||
a := f.impl.allocator
|
||||
free(f.impl.wname, a)
|
||||
delete(f.impl.name, a)
|
||||
free(f, a)
|
||||
return nil
|
||||
}
|
||||
|
||||
_name :: proc(fd: Handle, allocator := context.allocator) -> string {
|
||||
FILE_NAME_NORMALIZED :: 0x0
|
||||
handle := win32.HANDLE(fd)
|
||||
buf_len := win32.GetFinalPathNameByHandleW(handle, nil, 0, FILE_NAME_NORMALIZED)
|
||||
if buf_len == 0 {
|
||||
return ""
|
||||
|
||||
_close :: proc(f: ^File) -> Error {
|
||||
if f == nil {
|
||||
return nil
|
||||
}
|
||||
buf := make([]u16, buf_len, context.temp_allocator)
|
||||
n := win32.GetFinalPathNameByHandleW(handle, raw_data(buf), buf_len, FILE_NAME_NORMALIZED)
|
||||
return win32.utf16_to_utf8(buf[:n], allocator)
|
||||
if !win32.CloseHandle(win32.HANDLE(f.impl.fd)) {
|
||||
return .Closed
|
||||
}
|
||||
return _destroy(f)
|
||||
}
|
||||
|
||||
_seek :: proc(fd: Handle, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) {
|
||||
new_offset: win32.LARGE_INTEGER
|
||||
move_method: win32.DWORD
|
||||
_name :: proc(f: ^File) -> string {
|
||||
return f.impl.name if f != nil else ""
|
||||
}
|
||||
|
||||
_seek :: proc(f: ^File, offset: i64, whence: Seek_From) -> (ret: i64, err: Error) {
|
||||
handle := _handle(f)
|
||||
if handle == win32.INVALID_HANDLE {
|
||||
return 0, .Invalid_File
|
||||
}
|
||||
if f.impl.kind == .Pipe {
|
||||
return 0, .Invalid_File
|
||||
}
|
||||
|
||||
sync.guard(&f.impl.rw_mutex)
|
||||
|
||||
w: u32
|
||||
switch whence {
|
||||
case .Start: move_method = win32.FILE_BEGIN
|
||||
case .Current: move_method = win32.FILE_CURRENT
|
||||
case .End: move_method = win32.FILE_END
|
||||
case .Start: w = win32.FILE_BEGIN
|
||||
case .Current: w = win32.FILE_CURRENT
|
||||
case .End: w = win32.FILE_END
|
||||
}
|
||||
ok := win32.SetFilePointerEx(win32.HANDLE(fd), win32.LARGE_INTEGER(offset), &new_offset, move_method)
|
||||
ret = i64(new_offset)
|
||||
if !ok {
|
||||
err = .Invalid_Whence
|
||||
hi := i32(offset>>32)
|
||||
lo := i32(offset)
|
||||
|
||||
dw_ptr := win32.SetFilePointer(handle, lo, &hi, w)
|
||||
if dw_ptr == win32.INVALID_SET_FILE_POINTER {
|
||||
return 0, _get_platform_error()
|
||||
}
|
||||
return
|
||||
return i64(hi)<<32 + i64(dw_ptr), nil
|
||||
}
|
||||
|
||||
MAX_RW :: 1<<30
|
||||
_read :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
|
||||
read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Error) {
|
||||
if len(b) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
_read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Error) {
|
||||
if len(b) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
// TODO(bill): should this be moved to `_File` instead?
|
||||
BUF_SIZE :: 386
|
||||
buf16: [BUF_SIZE]u16
|
||||
buf8: [4*BUF_SIZE]u8
|
||||
|
||||
BUF_SIZE :: 386
|
||||
buf16: [BUF_SIZE]u16
|
||||
buf8: [4*BUF_SIZE]u8
|
||||
for n < len(b) && err == nil {
|
||||
min_read := max(len(b)/4, 1 if len(b) > 0 else 0)
|
||||
max_read := u32(min(BUF_SIZE, min_read))
|
||||
if max_read == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
for n < len(b) && err == nil {
|
||||
max_read := u32(min(BUF_SIZE, len(b)/4))
|
||||
single_read_length: u32
|
||||
ok := win32.ReadConsoleW(handle, &buf16[0], max_read, &single_read_length, nil)
|
||||
if !ok {
|
||||
err = _get_platform_error()
|
||||
}
|
||||
|
||||
single_read_length: u32
|
||||
err = _ok_or_error(win32.ReadConsoleW(handle, &buf16[0], max_read, &single_read_length, nil))
|
||||
buf8_len := utf16.decode_to_utf8(buf8[:], buf16[:single_read_length])
|
||||
src := buf8[:buf8_len]
|
||||
|
||||
buf8_len := utf16.decode_to_utf8(buf8[:], buf16[:single_read_length])
|
||||
src := buf8[:buf8_len]
|
||||
ctrl_z := false
|
||||
for i := 0; i < len(src) && n+i < len(b); i += 1 {
|
||||
x := src[i]
|
||||
if x == 0x1a { // ctrl-z
|
||||
ctrl_z = true
|
||||
break
|
||||
}
|
||||
b[n] = x
|
||||
n += 1
|
||||
}
|
||||
if ctrl_z || single_read_length < max_read {
|
||||
break
|
||||
}
|
||||
|
||||
ctrl_z := false
|
||||
for i := 0; i < len(src) && n+i < len(b); i += 1 {
|
||||
x := src[i]
|
||||
if x == 0x1a { // ctrl-z
|
||||
ctrl_z = true
|
||||
// NOTE(bill): if the last two values were a newline, then it is expected that
|
||||
// this is the end of the input
|
||||
if n >= 2 && single_read_length == max_read && string(b[n-2:n]) == "\r\n" {
|
||||
break
|
||||
}
|
||||
b[n] = x
|
||||
n += 1
|
||||
}
|
||||
if ctrl_z || single_read_length < len(buf16) {
|
||||
break
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
_read :: proc(fd: Handle, p: []byte) -> (n: int, err: Error) {
|
||||
if len(p) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
handle := win32.HANDLE(fd)
|
||||
|
||||
m: u32
|
||||
is_console := win32.GetConsoleMode(handle, &m)
|
||||
handle := _handle(f)
|
||||
|
||||
single_read_length: win32.DWORD
|
||||
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 is_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
|
||||
}
|
||||
|
||||
_read_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
pread :: proc(f: ^File, data: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
buf := data
|
||||
if len(buf) > MAX_RW {
|
||||
buf = buf[:MAX_RW]
|
||||
|
||||
_read_at :: proc(fd: Handle, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
if offset < 0 {
|
||||
return 0, .Invalid_Offset
|
||||
}
|
||||
curr_offset := seek(f, offset, .Current) or_return
|
||||
defer seek(f, curr_offset, .Start)
|
||||
|
||||
o := win32.OVERLAPPED{
|
||||
OffsetHigh = u32(offset>>32),
|
||||
Offset = u32(offset),
|
||||
}
|
||||
|
||||
// TODO(bill): Determine the correct behaviour for consoles
|
||||
|
||||
h := _handle(f)
|
||||
done: win32.DWORD
|
||||
if !win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
|
||||
err = _get_platform_error()
|
||||
done = 0
|
||||
}
|
||||
n = int(done)
|
||||
return
|
||||
}
|
||||
|
||||
b, offset := p, offset
|
||||
for len(b) > 0 {
|
||||
m := _pread(fd, b, offset) or_return
|
||||
sync.guard(&f.impl.p_mutex)
|
||||
|
||||
p, offset := p, offset
|
||||
for len(p) > 0 {
|
||||
m := pread(f, p, offset) or_return
|
||||
n += m
|
||||
b = b[m:]
|
||||
p = p[m:]
|
||||
offset += i64(m)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
_read_from :: proc(fd: Handle, r: io.Reader) -> (n: i64, err: Error) {
|
||||
_read_from :: proc(f: ^File, r: io.Reader) -> (n: i64, err: Error) {
|
||||
// TODO(bill)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
_pread :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
buf := data
|
||||
if len(buf) > MAX_RW {
|
||||
buf = buf[:MAX_RW]
|
||||
|
||||
}
|
||||
curr_offset := seek(fd, offset, .Current) or_return
|
||||
defer seek(fd, curr_offset, .Start)
|
||||
|
||||
o := win32.OVERLAPPED{
|
||||
OffsetHigh = u32(offset>>32),
|
||||
Offset = u32(offset),
|
||||
_write :: proc(f: ^File, p: []byte) -> (n: int, err: Error) {
|
||||
if len(p) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO(bill): Determine the correct behaviour for consoles
|
||||
single_write_length: win32.DWORD
|
||||
total_write: i64
|
||||
length := i64(len(p))
|
||||
|
||||
h := win32.HANDLE(fd)
|
||||
done: win32.DWORD
|
||||
_ok_or_error(win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o)) or_return
|
||||
return int(done), nil
|
||||
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))
|
||||
|
||||
e := win32.WriteFile(handle, &p[total_write], to_write, &single_write_length, nil)
|
||||
if single_write_length <= 0 || !e {
|
||||
n = int(total_write)
|
||||
err = _get_platform_error()
|
||||
return
|
||||
}
|
||||
total_write += i64(single_write_length)
|
||||
}
|
||||
return int(total_write), nil
|
||||
}
|
||||
|
||||
_pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
buf := data
|
||||
if len(buf) > MAX_RW {
|
||||
buf = buf[:MAX_RW]
|
||||
_write_at :: proc(f: ^File, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
pwrite :: proc(f: ^File, data: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
buf := data
|
||||
if len(buf) > MAX_RW {
|
||||
buf = buf[:MAX_RW]
|
||||
|
||||
}
|
||||
curr_offset := seek(fd, offset, .Current) or_return
|
||||
defer seek(fd, curr_offset, .Start)
|
||||
}
|
||||
curr_offset := seek(f, offset, .Current) or_return
|
||||
defer seek(f, curr_offset, .Start)
|
||||
|
||||
o := win32.OVERLAPPED{
|
||||
OffsetHigh = u32(offset>>32),
|
||||
Offset = u32(offset),
|
||||
o := win32.OVERLAPPED{
|
||||
OffsetHigh = u32(offset>>32),
|
||||
Offset = u32(offset),
|
||||
}
|
||||
|
||||
h := _handle(f)
|
||||
done: win32.DWORD
|
||||
if !win32.WriteFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
|
||||
err = _get_platform_error()
|
||||
done = 0
|
||||
}
|
||||
n = int(done)
|
||||
return
|
||||
}
|
||||
|
||||
h := win32.HANDLE(fd)
|
||||
done: win32.DWORD
|
||||
_ok_or_error(win32.WriteFile(h, raw_data(buf), u32(len(buf)), &done, &o)) or_return
|
||||
return int(done), nil
|
||||
}
|
||||
|
||||
_write :: proc(fd: Handle, p: []byte) -> (n: int, err: Error) {
|
||||
return
|
||||
}
|
||||
|
||||
_write_at :: proc(fd: Handle, p: []byte, offset: i64) -> (n: int, err: Error) {
|
||||
if offset < 0 {
|
||||
return 0, .Invalid_Offset
|
||||
}
|
||||
|
||||
b, offset := p, offset
|
||||
for len(b) > 0 {
|
||||
m := _pwrite(fd, b, offset) or_return
|
||||
sync.guard(&f.impl.p_mutex)
|
||||
p, offset := p, offset
|
||||
for len(p) > 0 {
|
||||
m := pwrite(f, p, offset) or_return
|
||||
n += m
|
||||
b = b[m:]
|
||||
p = p[m:]
|
||||
offset += i64(m)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
_write_to :: proc(fd: Handle, w: io.Writer) -> (n: i64, err: Error) {
|
||||
_write_to :: proc(f: ^File, w: io.Writer) -> (n: i64, err: Error) {
|
||||
// TODO(bill)
|
||||
return
|
||||
}
|
||||
|
||||
_file_size :: proc(fd: Handle) -> (n: i64, err: Error) {
|
||||
_file_size :: proc(f: ^File) -> (n: i64, err: Error) {
|
||||
length: win32.LARGE_INTEGER
|
||||
err = _ok_or_error(win32.GetFileSizeEx(win32.HANDLE(fd), &length))
|
||||
return i64(length), err
|
||||
handle := _handle(f)
|
||||
if !win32.GetFileSizeEx(handle, &length) {
|
||||
err = _get_platform_error()
|
||||
}
|
||||
n = i64(length)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
_sync :: proc(fd: Handle) -> Error {
|
||||
_sync :: proc(f: ^File) -> Error {
|
||||
return _flush(f)
|
||||
}
|
||||
|
||||
_flush :: proc(f: ^File) -> Error {
|
||||
handle := _handle(f)
|
||||
if !win32.FlushFileBuffers(handle) {
|
||||
return _get_platform_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_flush :: proc(fd: Handle) -> Error {
|
||||
return _ok_or_error(win32.FlushFileBuffers(win32.HANDLE(fd)))
|
||||
}
|
||||
|
||||
_truncate :: proc(fd: Handle, size: i64) -> Error {
|
||||
offset := seek(fd, size, .Start) or_return
|
||||
defer seek(fd, offset, .Start)
|
||||
|
||||
return _ok_or_error(win32.SetEndOfFile(win32.HANDLE(fd)))
|
||||
_truncate :: proc(f: ^File, size: i64) -> Error {
|
||||
if f == nil {
|
||||
return nil
|
||||
}
|
||||
curr_off := seek(f, 0, .Current) or_return
|
||||
defer seek(f, curr_off, .Start)
|
||||
seek(f, size, .Start) or_return
|
||||
handle := _handle(f)
|
||||
if !win32.SetEndOfFile(handle) {
|
||||
return _get_platform_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_remove :: proc(name: string) -> Error {
|
||||
p := win32.utf8_to_wstring(_fix_long_path(name))
|
||||
|
||||
err := _ok_or_error(win32.DeleteFileW(p))
|
||||
p := _fix_long_path(name)
|
||||
err, err1: Error
|
||||
if !win32.DeleteFileW(p) {
|
||||
err = _get_platform_error()
|
||||
}
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
err1 := _ok_or_error(win32.RemoveDirectoryW(p))
|
||||
if !win32.RemoveDirectoryW(p) {
|
||||
err1 = _get_platform_error()
|
||||
}
|
||||
if err1 == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -332,7 +464,10 @@ _remove :: proc(name: string) -> Error {
|
||||
err = err1
|
||||
} else if a & win32.FILE_ATTRIBUTE_READONLY != 0 {
|
||||
if win32.SetFileAttributesW(p, a &~ win32.FILE_ATTRIBUTE_READONLY) {
|
||||
err = _ok_or_error(win32.DeleteFileW(p))
|
||||
err = nil
|
||||
if !win32.DeleteFileW(p) {
|
||||
err = _get_platform_error()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -342,80 +477,253 @@ _remove :: proc(name: string) -> Error {
|
||||
}
|
||||
|
||||
_rename :: proc(old_path, new_path: string) -> Error {
|
||||
from := win32.utf8_to_wstring(old_path, context.temp_allocator)
|
||||
to := win32.utf8_to_wstring(new_path, context.temp_allocator)
|
||||
return _ok_or_error(win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING))
|
||||
from := _fix_long_path(old_path)
|
||||
to := _fix_long_path(new_path)
|
||||
if win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING) {
|
||||
return nil
|
||||
}
|
||||
return _get_platform_error()
|
||||
|
||||
}
|
||||
|
||||
|
||||
_link :: proc(old_name, new_name: string) -> Error {
|
||||
n := win32.utf8_to_wstring(_fix_long_path(new_name))
|
||||
o := win32.utf8_to_wstring(_fix_long_path(old_name))
|
||||
return _ok_or_error(win32.CreateHardLinkW(n, o, nil))
|
||||
o := _fix_long_path(old_name)
|
||||
n := _fix_long_path(new_name)
|
||||
if win32.CreateHardLinkW(n, o, nil) {
|
||||
return nil
|
||||
}
|
||||
return _get_platform_error()
|
||||
}
|
||||
|
||||
_symlink :: proc(old_name, new_name: string) -> Error {
|
||||
return nil
|
||||
return .Unsupported
|
||||
}
|
||||
|
||||
_read_link :: proc(name: string) -> (string, Error) {
|
||||
_open_sym_link :: proc(p: [^]u16) -> (handle: win32.HANDLE, err: Error) {
|
||||
attrs := u32(win32.FILE_FLAG_BACKUP_SEMANTICS)
|
||||
attrs |= win32.FILE_FLAG_OPEN_REPARSE_POINT
|
||||
handle = win32.CreateFileW(p, 0, 0, nil, win32.OPEN_EXISTING, attrs, nil)
|
||||
if handle == win32.INVALID_HANDLE {
|
||||
return nil, _get_platform_error()
|
||||
}
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
_normalize_link_path :: proc(p: []u16, allocator: runtime.Allocator) -> (str: string, err: Error) {
|
||||
has_prefix :: proc(p: []u16, str: string) -> bool {
|
||||
if len(p) < len(str) {
|
||||
return false
|
||||
}
|
||||
// assume ascii
|
||||
for i in 0..<len(str) {
|
||||
if p[i] != u16(str[i]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
has_unc_prefix :: proc(p: []u16) -> bool {
|
||||
return has_prefix(p, `\??\`)
|
||||
}
|
||||
|
||||
if !has_unc_prefix(p) {
|
||||
return win32.utf16_to_utf8(p, allocator)
|
||||
}
|
||||
|
||||
ws := p[4:]
|
||||
switch {
|
||||
case len(ws) >= 2 && ws[1] == ':':
|
||||
return win32.utf16_to_utf8(ws, allocator)
|
||||
case has_prefix(ws, `UNC\`):
|
||||
ws[3] = '\\' // override data in buffer
|
||||
return win32.utf16_to_utf8(ws[3:], allocator)
|
||||
}
|
||||
|
||||
|
||||
handle := _open_sym_link(raw_data(p)) or_return
|
||||
defer win32.CloseHandle(handle)
|
||||
|
||||
n := win32.GetFinalPathNameByHandleW(handle, nil, 0, win32.VOLUME_NAME_DOS)
|
||||
if n == 0 {
|
||||
return "", _get_platform_error()
|
||||
}
|
||||
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()
|
||||
}
|
||||
|
||||
ws = buf[:n]
|
||||
if has_unc_prefix(ws) {
|
||||
ws = ws[4:]
|
||||
if len(ws) > 3 && has_prefix(ws, `UNC`) {
|
||||
ws[2] = '\\'
|
||||
return win32.utf16_to_utf8(ws[2:], allocator)
|
||||
}
|
||||
return win32.utf16_to_utf8(ws, allocator)
|
||||
}
|
||||
return "", .Invalid_Path
|
||||
}
|
||||
|
||||
_read_link :: proc(name: string, allocator: runtime.Allocator) -> (s: string, err: Error) {
|
||||
MAXIMUM_REPARSE_DATA_BUFFER_SIZE :: 16 * 1024
|
||||
|
||||
@thread_local
|
||||
rdb_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]byte
|
||||
|
||||
p := _fix_long_path(name)
|
||||
handle := _open_sym_link(p) or_return
|
||||
defer win32.CloseHandle(handle)
|
||||
|
||||
bytes_returned: u32
|
||||
if !win32.DeviceIoControl(handle, win32.FSCTL_GET_REPARSE_POINT, nil, 0, &rdb_buf[0], len(rdb_buf)-1, &bytes_returned, nil) {
|
||||
err = _get_platform_error()
|
||||
return
|
||||
}
|
||||
mem.zero_slice(rdb_buf[:min(bytes_returned+1, len(rdb_buf))])
|
||||
|
||||
|
||||
rdb := (^win32.REPARSE_DATA_BUFFER)(&rdb_buf[0])
|
||||
switch rdb.ReparseTag {
|
||||
case win32.IO_REPARSE_TAG_SYMLINK:
|
||||
rb := (^win32.SYMBOLIC_LINK_REPARSE_BUFFER)(&rdb.rest)
|
||||
pb := win32.wstring(&rb.PathBuffer)
|
||||
pb[rb.SubstituteNameOffset+rb.SubstituteNameLength] = 0
|
||||
p := pb[rb.SubstituteNameOffset:][:rb.SubstituteNameLength]
|
||||
if rb.Flags & win32.SYMLINK_FLAG_RELATIVE != 0 {
|
||||
return win32.utf16_to_utf8(p, allocator)
|
||||
}
|
||||
return _normalize_link_path(p, allocator)
|
||||
|
||||
case win32.IO_REPARSE_TAG_MOUNT_POINT:
|
||||
rb := (^win32.MOUNT_POINT_REPARSE_BUFFER)(&rdb.rest)
|
||||
pb := win32.wstring(&rb.PathBuffer)
|
||||
pb[rb.SubstituteNameOffset+rb.SubstituteNameLength] = 0
|
||||
p := pb[rb.SubstituteNameOffset:][:rb.SubstituteNameLength]
|
||||
return _normalize_link_path(p, allocator)
|
||||
}
|
||||
// Path wasn't a symlink/junction but another reparse point kind
|
||||
return "", nil
|
||||
}
|
||||
|
||||
_unlink :: proc(path: string) -> Error {
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
return _ok_or_error(win32.DeleteFileW(wpath))
|
||||
}
|
||||
|
||||
|
||||
_chdir :: proc(fd: Handle) -> Error {
|
||||
_fchdir :: proc(f: ^File) -> Error {
|
||||
if f == nil {
|
||||
return nil
|
||||
}
|
||||
if !win32.SetCurrentDirectoryW(f.impl.wname) {
|
||||
return _get_platform_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_chmod :: proc(fd: Handle, mode: File_Mode) -> Error {
|
||||
_fchmod :: proc(f: ^File, mode: File_Mode) -> Error {
|
||||
if f == nil {
|
||||
return nil
|
||||
}
|
||||
d: win32.BY_HANDLE_FILE_INFORMATION
|
||||
if !win32.GetFileInformationByHandle(_handle(f), &d) {
|
||||
return _get_platform_error()
|
||||
}
|
||||
attrs := d.dwFileAttributes
|
||||
if mode & S_IWRITE != 0 {
|
||||
attrs &~= win32.FILE_ATTRIBUTE_READONLY
|
||||
} else {
|
||||
attrs |= win32.FILE_ATTRIBUTE_READONLY
|
||||
}
|
||||
|
||||
info: win32.FILE_BASIC_INFO
|
||||
info.FileAttributes = attrs
|
||||
if !win32.SetFileInformationByHandle(_handle(f), .FileBasicInfo, &info, size_of(d)) {
|
||||
return _get_platform_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_chown :: proc(fd: Handle, uid, gid: int) -> Error {
|
||||
_fchown :: proc(f: ^File, uid, gid: int) -> Error {
|
||||
return .Unsupported
|
||||
}
|
||||
|
||||
_chdir :: proc(name: string) -> Error {
|
||||
p := _fix_long_path(name)
|
||||
if !win32.SetCurrentDirectoryW(p) {
|
||||
return _get_platform_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_chmod :: proc(name: string, mode: File_Mode) -> Error {
|
||||
f := open(name, {.Write}) or_return
|
||||
defer close(f)
|
||||
return _fchmod(f, mode)
|
||||
}
|
||||
|
||||
_chown :: proc(name: string, uid, gid: int) -> Error {
|
||||
return .Unsupported
|
||||
}
|
||||
|
||||
_lchown :: proc(name: string, uid, gid: int) -> Error {
|
||||
return nil
|
||||
return .Unsupported
|
||||
}
|
||||
|
||||
|
||||
_chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
|
||||
f := open(name, {.Write}) or_return
|
||||
defer close(f)
|
||||
return _fchtimes(f, atime, mtime)
|
||||
}
|
||||
_fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
|
||||
if f == nil {
|
||||
return nil
|
||||
}
|
||||
d: win32.BY_HANDLE_FILE_INFORMATION
|
||||
if !win32.GetFileInformationByHandle(_handle(f), &d) {
|
||||
return _get_platform_error()
|
||||
}
|
||||
|
||||
to_windows_time :: #force_inline proc(t: time.Time) -> win32.LARGE_INTEGER {
|
||||
// a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC)
|
||||
return win32.LARGE_INTEGER(time.time_to_unix_nano(t) * 100 + 116444736000000000)
|
||||
}
|
||||
|
||||
atime, mtime := atime, mtime
|
||||
if time.time_to_unix_nano(atime) < time.time_to_unix_nano(mtime) {
|
||||
atime = mtime
|
||||
}
|
||||
|
||||
info: win32.FILE_BASIC_INFO
|
||||
info.LastAccessTime = to_windows_time(atime)
|
||||
info.LastWriteTime = to_windows_time(mtime)
|
||||
if !win32.SetFileInformationByHandle(_handle(f), .FileBasicInfo, &info, size_of(d)) {
|
||||
return _get_platform_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
|
||||
_exists :: proc(path: string) -> bool {
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
return bool(win32.PathFileExistsW(wpath))
|
||||
wpath := _fix_long_path(path)
|
||||
attribs := win32.GetFileAttributesW(wpath)
|
||||
return i32(attribs) != win32.INVALID_FILE_ATTRIBUTES
|
||||
}
|
||||
|
||||
_is_file :: proc(fd: Handle) -> bool {
|
||||
hnd := win32.HANDLE(fd)
|
||||
|
||||
file_info: win32.BY_HANDLE_FILE_INFORMATION
|
||||
if ok := win32.GetFileInformationByHandle(hnd, &file_info); !ok {
|
||||
return false
|
||||
_is_file :: proc(path: string) -> bool {
|
||||
wpath := _fix_long_path(path)
|
||||
attribs := win32.GetFileAttributesW(wpath)
|
||||
if i32(attribs) != win32.INVALID_FILE_ATTRIBUTES {
|
||||
return attribs & win32.FILE_ATTRIBUTE_DIRECTORY == 0
|
||||
}
|
||||
no_flags :: win32.FILE_ATTRIBUTE_DIRECTORY | win32.FILE_ATTRIBUTE_DEVICE
|
||||
yes_flags :: win32.FILE_ATTRIBUTE_NORMAL
|
||||
return (file_info.dwFileAttributes & no_flags == 0) && (file_info.dwFileAttributes & yes_flags != 0)
|
||||
return false
|
||||
}
|
||||
|
||||
_is_dir :: proc(fd: Handle) -> bool {
|
||||
hnd := win32.HANDLE(fd)
|
||||
|
||||
file_info: win32.BY_HANDLE_FILE_INFORMATION
|
||||
if ok := win32.GetFileInformationByHandle(hnd, &file_info); !ok {
|
||||
return false
|
||||
_is_dir :: proc(path: string) -> bool {
|
||||
wpath := _fix_long_path(path)
|
||||
attribs := win32.GetFileAttributesW(wpath)
|
||||
if i32(attribs) != win32.INVALID_FILE_ATTRIBUTES {
|
||||
return attribs & win32.FILE_ATTRIBUTE_DIRECTORY != 0
|
||||
}
|
||||
return file_info.dwFileAttributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -102,7 +102,6 @@ MMAP_PROT :: unix.PROT_READ | unix.PROT_WRITE
|
||||
|
||||
|
||||
@thread_local _local_region: ^Region
|
||||
//_local_region: ^Region
|
||||
global_regions: ^Region
|
||||
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
+41
-23
@@ -2,6 +2,8 @@
|
||||
package os2
|
||||
|
||||
import "core:strings"
|
||||
import "core:strconv"
|
||||
import "core:runtime"
|
||||
import "core:sys/unix"
|
||||
|
||||
_Path_Separator :: '/'
|
||||
@@ -37,31 +39,31 @@ _mkdir :: proc(path: string, perm: File_Mode) -> Error {
|
||||
}
|
||||
|
||||
_mkdir_all :: proc(path: string, perm: File_Mode) -> Error {
|
||||
_mkdirat :: proc(dfd: Handle, path: []u8, perm: int, has_created: ^bool) -> Error {
|
||||
_mkdirat :: proc(dfd: int, path: []u8, perm: int, has_created: ^bool) -> Error {
|
||||
if len(path) == 0 {
|
||||
return _ok_or_error(unix.sys_close(int(dfd)))
|
||||
return _ok_or_error(unix.sys_close(dfd))
|
||||
}
|
||||
i: int
|
||||
for /**/; i < len(path) - 1 && path[i] != '/'; i += 1 {}
|
||||
path[i] = 0
|
||||
new_dfd := unix.sys_openat(int(dfd), cstring(&path[0]), _OPENDIR_FLAGS)
|
||||
new_dfd := unix.sys_openat(dfd, cstring(&path[0]), _OPENDIR_FLAGS)
|
||||
switch new_dfd {
|
||||
case -ENOENT:
|
||||
if res := unix.sys_mkdirat(int(dfd), cstring(&path[0]), perm); res < 0 {
|
||||
if res := unix.sys_mkdirat(dfd, cstring(&path[0]), perm); res < 0 {
|
||||
return _get_platform_error(res)
|
||||
}
|
||||
has_created^ = true
|
||||
if new_dfd = unix.sys_openat(int(dfd), cstring(&path[0]), _OPENDIR_FLAGS); new_dfd < 0 {
|
||||
if new_dfd = unix.sys_openat(dfd, cstring(&path[0]), _OPENDIR_FLAGS); new_dfd < 0 {
|
||||
return _get_platform_error(new_dfd)
|
||||
}
|
||||
fallthrough
|
||||
case 0:
|
||||
if res := unix.sys_close(int(dfd)); res < 0 {
|
||||
if res := unix.sys_close(dfd); res < 0 {
|
||||
return _get_platform_error(res)
|
||||
}
|
||||
// skip consecutive '/'
|
||||
for i += 1; i < len(path) && path[i] == '/'; i += 1 {}
|
||||
return _mkdirat(Handle(new_dfd), path[i:], perm, has_created)
|
||||
return _mkdirat(new_dfd, path[i:], perm, has_created)
|
||||
case:
|
||||
return _get_platform_error(new_dfd)
|
||||
}
|
||||
@@ -101,7 +103,7 @@ _mkdir_all :: proc(path: string, perm: File_Mode) -> Error {
|
||||
}
|
||||
|
||||
has_created: bool
|
||||
_mkdirat(Handle(dfd), path_bytes, int(perm & 0o777), &has_created) or_return
|
||||
_mkdirat(dfd, path_bytes, int(perm & 0o777), &has_created) or_return
|
||||
if has_created {
|
||||
return nil
|
||||
}
|
||||
@@ -120,13 +122,13 @@ dirent64 :: struct {
|
||||
_remove_all :: proc(path: string) -> Error {
|
||||
DT_DIR :: 4
|
||||
|
||||
_remove_all_dir :: proc(dfd: Handle) -> Error {
|
||||
_remove_all_dir :: proc(dfd: int) -> Error {
|
||||
n := 64
|
||||
buf := make([]u8, n)
|
||||
defer delete(buf)
|
||||
|
||||
loop: for {
|
||||
getdents_res := unix.sys_getdents64(int(dfd), &buf[0], n)
|
||||
getdents_res := unix.sys_getdents64(dfd, &buf[0], n)
|
||||
switch getdents_res {
|
||||
case -EINVAL:
|
||||
delete(buf)
|
||||
@@ -161,15 +163,15 @@ _remove_all :: proc(path: string) -> Error {
|
||||
|
||||
switch d.d_type {
|
||||
case DT_DIR:
|
||||
handle_i := unix.sys_openat(int(dfd), d_name_cstr, _OPENDIR_FLAGS)
|
||||
if handle_i < 0 {
|
||||
return _get_platform_error(handle_i)
|
||||
new_dfd := unix.sys_openat(dfd, d_name_cstr, _OPENDIR_FLAGS)
|
||||
if new_dfd < 0 {
|
||||
return _get_platform_error(new_dfd)
|
||||
}
|
||||
defer unix.sys_close(handle_i)
|
||||
_remove_all_dir(Handle(handle_i)) or_return
|
||||
unlink_res = unix.sys_unlinkat(int(dfd), d_name_cstr, int(unix.AT_REMOVEDIR))
|
||||
defer unix.sys_close(new_dfd)
|
||||
_remove_all_dir(new_dfd) or_return
|
||||
unlink_res = unix.sys_unlinkat(dfd, d_name_cstr, int(unix.AT_REMOVEDIR))
|
||||
case:
|
||||
unlink_res = unix.sys_unlinkat(int(dfd), d_name_cstr)
|
||||
unlink_res = unix.sys_unlinkat(dfd, d_name_cstr)
|
||||
}
|
||||
|
||||
if unlink_res < 0 {
|
||||
@@ -185,21 +187,20 @@ _remove_all :: proc(path: string) -> Error {
|
||||
delete(path_cstr)
|
||||
}
|
||||
|
||||
handle_i := unix.sys_open(path_cstr, _OPENDIR_FLAGS)
|
||||
switch handle_i {
|
||||
fd := unix.sys_open(path_cstr, _OPENDIR_FLAGS)
|
||||
switch fd {
|
||||
case -ENOTDIR:
|
||||
return _ok_or_error(unix.sys_unlink(path_cstr))
|
||||
case -4096..<0:
|
||||
return _get_platform_error(handle_i)
|
||||
return _get_platform_error(fd)
|
||||
}
|
||||
|
||||
fd := Handle(handle_i)
|
||||
defer close(fd)
|
||||
defer unix.sys_close(fd)
|
||||
_remove_all_dir(fd) or_return
|
||||
return _ok_or_error(unix.sys_rmdir(path_cstr))
|
||||
}
|
||||
|
||||
_getwd :: proc(allocator := context.allocator) -> (string, Error) {
|
||||
_getwd :: proc(allocator: runtime.Allocator) -> (string, Error) {
|
||||
// NOTE(tetra): I would use PATH_MAX here, but I was not able to find
|
||||
// an authoritative value for it across all systems.
|
||||
// The largest value I could find was 4096, so might as well use the page size.
|
||||
@@ -227,3 +228,20 @@ _setwd :: proc(dir: string) -> Error {
|
||||
}
|
||||
return _ok_or_error(unix.sys_chdir(dir_cstr))
|
||||
}
|
||||
|
||||
_get_full_path :: proc(fd: int, allocator := context.allocator) -> string {
|
||||
PROC_FD_PATH :: "/proc/self/fd/"
|
||||
|
||||
buf: [32]u8
|
||||
copy(buf[:], PROC_FD_PATH)
|
||||
|
||||
strconv.itoa(buf[len(PROC_FD_PATH):], fd)
|
||||
|
||||
fullpath: string
|
||||
err: Error
|
||||
if fullpath, err = _read_link_cstr(cstring(&buf[0]), allocator); err != nil || fullpath[0] != '/' {
|
||||
return ""
|
||||
}
|
||||
return fullpath
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
//+private
|
||||
package os2
|
||||
|
||||
import win32 "core:sys/windows"
|
||||
import "core:runtime"
|
||||
import "core:strings"
|
||||
|
||||
_Path_Separator :: '\\'
|
||||
_Path_List_Separator :: ';'
|
||||
|
||||
@@ -9,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
|
||||
}
|
||||
|
||||
@@ -22,10 +73,90 @@ _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
|
||||
}
|
||||
|
||||
|
||||
can_use_long_paths: bool
|
||||
|
||||
@(init)
|
||||
init_long_path_support :: proc() {
|
||||
// TODO(bill): init_long_path_support
|
||||
// ADD THIS SHIT
|
||||
// registry_path := win32.L(`Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\FileSystem\LongPathsEnabled`)
|
||||
can_use_long_paths = false
|
||||
}
|
||||
|
||||
|
||||
_fix_long_path_slice :: proc(path: string) -> []u16 {
|
||||
return win32.utf8_to_utf16(_fix_long_path_internal(path))
|
||||
}
|
||||
|
||||
_fix_long_path :: proc(path: string) -> win32.wstring {
|
||||
return win32.utf8_to_wstring(_fix_long_path_internal(path))
|
||||
}
|
||||
|
||||
|
||||
_fix_long_path_internal :: proc(path: string) -> string {
|
||||
if can_use_long_paths {
|
||||
return path
|
||||
}
|
||||
|
||||
// When using win32 to create a directory, the path
|
||||
// cannot be too long that you cannot append an 8.3
|
||||
// file name, because MAX_PATH is 260, 260-12 = 248
|
||||
if len(path) < 248 {
|
||||
return path
|
||||
}
|
||||
|
||||
// UNC paths do not need to be modified
|
||||
if len(path) >= 2 && path[:2] == `\\` {
|
||||
return path
|
||||
}
|
||||
|
||||
if !_is_abs(path) { // relative path
|
||||
return path
|
||||
}
|
||||
|
||||
PREFIX :: `\\?`
|
||||
path_buf := make([]byte, len(PREFIX)+len(path)+1, _temp_allocator())
|
||||
copy(path_buf, PREFIX)
|
||||
n := len(path)
|
||||
r, w := 0, len(PREFIX)
|
||||
for r < n {
|
||||
switch {
|
||||
case is_path_separator(path[r]):
|
||||
r += 1
|
||||
case path[r] == '.' && (r+1 == n || is_path_separator(path[r+1])):
|
||||
// \.\
|
||||
r += 1
|
||||
case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || is_path_separator(path[r+2])):
|
||||
// Skip \..\ paths
|
||||
return path
|
||||
case:
|
||||
path_buf[w] = '\\'
|
||||
w += 1
|
||||
for r < n && !is_path_separator(path[r]) {
|
||||
path_buf[w] = path[r]
|
||||
r += 1
|
||||
w += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Root directories require a trailing \
|
||||
if w == len(`\\?\c:`) {
|
||||
path_buf[w] = '\\'
|
||||
w += 1
|
||||
}
|
||||
|
||||
return string(path_buf[:w])
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
package os2
|
||||
|
||||
pipe :: proc() -> (r, w: Handle, err: Error) {
|
||||
pipe :: proc() -> (r, w: ^File, err: Error) {
|
||||
return _pipe()
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//+private
|
||||
package os2
|
||||
|
||||
_pipe :: proc() -> (r, w: Handle, err: Error) {
|
||||
return INVALID_HANDLE, INVALID_HANDLE, nil
|
||||
_pipe :: proc() -> (r, w: ^File, err: Error) {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -3,15 +3,11 @@ package os2
|
||||
|
||||
import win32 "core:sys/windows"
|
||||
|
||||
_pipe :: proc() -> (r, w: Handle, err: Error) {
|
||||
sa: win32.SECURITY_ATTRIBUTES
|
||||
sa.nLength = size_of(win32.SECURITY_ATTRIBUTES)
|
||||
sa.bInheritHandle = true
|
||||
|
||||
_pipe :: proc() -> (r, w: ^File, err: Error) {
|
||||
p: [2]win32.HANDLE
|
||||
if !win32.CreatePipe(&p[0], &p[1], &sa, 0) {
|
||||
return 0, 0, Platform_Error{i32(win32.GetLastError())}
|
||||
if !win32.CreatePipe(&p[0], &p[1], nil, 0) {
|
||||
return nil, nil, _get_platform_error()
|
||||
}
|
||||
return Handle(p[0]), Handle(p[1]), nil
|
||||
return new_file(uintptr(p[0]), ""), new_file(uintptr(p[1]), ""), nil
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ Process :: struct {
|
||||
Process_Attributes :: struct {
|
||||
dir: string,
|
||||
env: []string,
|
||||
files: []Handle,
|
||||
files: []^File,
|
||||
sys: ^Process_Attributes_OS_Specific,
|
||||
}
|
||||
|
||||
|
||||
@@ -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(fd: Handle, allocator := context.allocator) -> (File_Info, Error) {
|
||||
return _fstat(fd, allocator)
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
package os2
|
||||
|
||||
import "core:time"
|
||||
import "core:runtime"
|
||||
import "core:sys/unix"
|
||||
import "core:path/filepath"
|
||||
|
||||
@@ -59,7 +60,7 @@ Unix_File_Time :: struct {
|
||||
}
|
||||
|
||||
@private
|
||||
OS_Stat :: struct {
|
||||
_Stat :: struct {
|
||||
device_id: u64, // ID of device containing file
|
||||
serial: u64, // File serial number
|
||||
nlink: u64, // Number of hard links
|
||||
@@ -82,16 +83,20 @@ OS_Stat :: struct {
|
||||
}
|
||||
|
||||
|
||||
_fstat :: proc(fd: Handle, allocator := context.allocator) -> (File_Info, Error) {
|
||||
s: OS_Stat
|
||||
result := unix.sys_fstat(int(fd), &s)
|
||||
_fstat :: proc(f: ^File, allocator := context.allocator) -> (File_Info, Error) {
|
||||
return _fstat_internal(f.impl.fd, allocator)
|
||||
}
|
||||
|
||||
_fstat_internal :: proc(fd: int, allocator: runtime.Allocator) -> (File_Info, Error) {
|
||||
s: _Stat
|
||||
result := unix.sys_fstat(fd, &s)
|
||||
if result < 0 {
|
||||
return {}, _get_platform_error(result)
|
||||
}
|
||||
|
||||
// TODO: As of Linux 4.11, the new statx syscall can retrieve creation_time
|
||||
fi := File_Info {
|
||||
fullpath = _name(fd, allocator),
|
||||
fullpath = _get_full_path(fd, allocator),
|
||||
name = "",
|
||||
size = s.size,
|
||||
mode = 0,
|
||||
@@ -117,7 +122,7 @@ _stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error
|
||||
return {}, _get_platform_error(fd)
|
||||
}
|
||||
defer unix.sys_close(fd)
|
||||
return _fstat(Handle(fd), allocator)
|
||||
return _fstat_internal(fd, allocator)
|
||||
}
|
||||
|
||||
_lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Error) {
|
||||
@@ -130,14 +135,14 @@ _lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Erro
|
||||
return {}, _get_platform_error(fd)
|
||||
}
|
||||
defer unix.sys_close(fd)
|
||||
return _fstat(Handle(fd), allocator)
|
||||
return _fstat_internal(fd, allocator)
|
||||
}
|
||||
|
||||
_same_file :: proc(fi1, fi2: File_Info) -> bool {
|
||||
return fi1.fullpath == fi2.fullpath
|
||||
}
|
||||
|
||||
_stat_internal :: proc(name: string) -> (s: OS_Stat, res: int) {
|
||||
_stat_internal :: proc(name: string) -> (s: _Stat, res: int) {
|
||||
name_cstr, allocated := _name_to_cstring(name)
|
||||
defer if allocated {
|
||||
delete(name_cstr)
|
||||
|
||||
+131
-127
@@ -1,21 +1,22 @@
|
||||
//+private
|
||||
package os2
|
||||
|
||||
import "core:runtime"
|
||||
import "core:time"
|
||||
import "core:strings"
|
||||
import win32 "core:sys/windows"
|
||||
|
||||
_fstat :: proc(fd: Handle, allocator := context.allocator) -> (File_Info, Error) {
|
||||
if fd == 0 {
|
||||
return {}, .Invalid_Argument
|
||||
_fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) {
|
||||
if f == nil || f.impl.fd == nil {
|
||||
return {}, nil
|
||||
}
|
||||
context.allocator = allocator
|
||||
|
||||
path, err := _cleanpath_from_handle(fd)
|
||||
path, err := _cleanpath_from_handle(f, allocator)
|
||||
if err != nil {
|
||||
return {}, err
|
||||
}
|
||||
|
||||
h := win32.HANDLE(fd)
|
||||
h := _handle(f)
|
||||
switch win32.GetFileType(h) {
|
||||
case win32.FILE_TYPE_PIPE, win32.FILE_TYPE_CHAR:
|
||||
fi: File_Info
|
||||
@@ -25,58 +26,52 @@ _fstat :: proc(fd: Handle, 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
|
||||
}
|
||||
|
||||
|
||||
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 "", _get_platform_error()
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
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 := win32.utf8_to_wstring(_fix_long_path(name), context.temp_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()
|
||||
@@ -90,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)
|
||||
@@ -99,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)
|
||||
}
|
||||
|
||||
|
||||
@@ -124,56 +119,40 @@ _cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 {
|
||||
}
|
||||
|
||||
|
||||
_cleanpath_from_handle :: proc(fd: Handle) -> (string, Error) {
|
||||
if fd == 0 {
|
||||
return "", .Invalid_Argument
|
||||
_cleanpath_from_handle :: proc(f: ^File, allocator: runtime.Allocator) -> (string, Error) {
|
||||
if f == nil || f.impl.fd == nil {
|
||||
return "", nil
|
||||
}
|
||||
h := win32.HANDLE(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 "", Platform_Error{i32(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)
|
||||
}
|
||||
|
||||
_cleanpath_from_handle_u16 :: proc(fd: Handle) -> ([]u16, Error) {
|
||||
if fd == 0 {
|
||||
return nil, .Invalid_Argument
|
||||
_cleanpath_from_handle_u16 :: proc(f: ^File) -> ([]u16, Error) {
|
||||
if f == nil || f.impl.fd == nil {
|
||||
return nil, nil
|
||||
}
|
||||
h := win32.HANDLE(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, Platform_Error{i32(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, runtime.Allocator_Error) {
|
||||
buf := buf
|
||||
buf = _cleanpath_strip_prefix(buf)
|
||||
return win32.utf16_to_utf8(buf, context.allocator)
|
||||
return win32.utf16_to_utf8(buf, allocator)
|
||||
}
|
||||
|
||||
|
||||
@@ -215,15 +194,15 @@ file_type_mode :: proc(h: win32.HANDLE) -> File_Mode {
|
||||
|
||||
|
||||
|
||||
_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 {
|
||||
_file_mode_from_file_attributes :: proc(file_attributes: win32.DWORD, h: win32.HANDLE, ReparseTag: win32.DWORD) -> (mode: File_Mode) {
|
||||
if file_attributes & win32.FILE_ATTRIBUTE_READONLY != 0 {
|
||||
mode |= 0o444
|
||||
} else {
|
||||
mode |= 0o666
|
||||
}
|
||||
|
||||
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
|
||||
@@ -232,7 +211,7 @@ _file_mode_from_file_attributes :: proc(FileAttributes: win32.DWORD, h: win32.HA
|
||||
if is_sym {
|
||||
mode |= File_Mode_Sym_Link
|
||||
} else {
|
||||
if FileAttributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||
if file_attributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
|
||||
mode |= 0o111 | File_Mode_Dir
|
||||
}
|
||||
|
||||
@@ -245,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)
|
||||
@@ -255,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)
|
||||
@@ -272,14 +251,14 @@ _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 {}, _get_platform_error()
|
||||
@@ -290,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 {}, Platform_Error{i32(err)}
|
||||
return {}, Platform_Error(err)
|
||||
}
|
||||
// Indicate this is a symlink on FAT file systems
|
||||
ti.ReparseTag = 0
|
||||
@@ -312,58 +291,83 @@ _file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HA
|
||||
return fi, nil
|
||||
}
|
||||
|
||||
_is_abs :: proc(path: string) -> bool {
|
||||
if len(path) > 0 && path[0] == '/' {
|
||||
return true
|
||||
|
||||
|
||||
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
|
||||
}
|
||||
if len(path) > 2 {
|
||||
switch path[0] {
|
||||
case 'A'..='Z', 'a'..='z':
|
||||
return path[1] == ':' && is_path_separator(path[2])
|
||||
for reserved in reserved_names {
|
||||
if strings.equal_fold(path, reserved) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
_fix_long_path :: proc(path: string) -> string {
|
||||
if len(path) < 248 {
|
||||
return path
|
||||
}
|
||||
_is_UNC :: proc(path: string) -> bool {
|
||||
return _volume_name_len(path) > 2
|
||||
}
|
||||
|
||||
if len(path) >= 2 && path[:2] == `\\` {
|
||||
return path
|
||||
}
|
||||
if !_is_abs(path) {
|
||||
return path
|
||||
}
|
||||
_volume_name_len :: proc(path: string) -> int {
|
||||
if ODIN_OS == .Windows {
|
||||
if len(path) < 2 {
|
||||
return 0
|
||||
}
|
||||
c := path[0]
|
||||
if path[1] == ':' {
|
||||
switch c {
|
||||
case 'a'..='z', 'A'..='Z':
|
||||
return 2
|
||||
}
|
||||
}
|
||||
|
||||
prefix :: `\\?`
|
||||
|
||||
path_buf := make([]byte, len(prefix)+len(path)+len(`\`), context.temp_allocator)
|
||||
copy(path_buf, prefix)
|
||||
n := len(path)
|
||||
r, w := 0, len(prefix)
|
||||
for r < n {
|
||||
switch {
|
||||
case is_path_separator(path[r]):
|
||||
r += 1
|
||||
case path[r] == '.' && (r+1 == n || is_path_separator(path[r+1])):
|
||||
r += 1
|
||||
case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || is_path_separator(path[r+2])):
|
||||
return path
|
||||
case:
|
||||
path_buf[w] = '\\'
|
||||
w += 1
|
||||
for ; r < n && !is_path_separator(path[r]); r += 1 {
|
||||
path_buf[w] = path[r]
|
||||
w += 1
|
||||
// URL: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
|
||||
if l := len(path); l >= 5 && _is_path_separator(path[0]) && _is_path_separator(path[1]) &&
|
||||
!_is_path_separator(path[2]) && path[2] != '.' {
|
||||
for n := 3; n < l-1; n += 1 {
|
||||
if _is_path_separator(path[n]) {
|
||||
n += 1
|
||||
if !_is_path_separator(path[n]) {
|
||||
if path[n] == '.' {
|
||||
break
|
||||
}
|
||||
}
|
||||
for ; n < l; n += 1 {
|
||||
if _is_path_separator(path[n]) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if w == len(`\\?\c:`) {
|
||||
path_buf[w] = '\\'
|
||||
w += 1
|
||||
}
|
||||
return string(path_buf[:w])
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
_is_abs :: proc(path: string) -> bool {
|
||||
if _is_reserved_name(path) {
|
||||
return true
|
||||
}
|
||||
l := _volume_name_len(path)
|
||||
if l == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
path := path
|
||||
path = path[l:]
|
||||
if path == "" {
|
||||
return false
|
||||
}
|
||||
return is_path_separator(path[0])
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
package os2
|
||||
|
||||
import "core:runtime"
|
||||
|
||||
create_temp :: proc(dir, pattern: string) -> (Handle, Error) {
|
||||
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, Error) {
|
||||
return _temp_dir(allocator)
|
||||
}
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
//+private
|
||||
package os2
|
||||
|
||||
import "core:runtime"
|
||||
|
||||
_create_temp :: proc(dir, pattern: string) -> (Handle, Error) {
|
||||
|
||||
_create_temp :: proc(dir, pattern: string) -> (^File, Error) {
|
||||
//TODO
|
||||
return 0, nil
|
||||
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) {
|
||||
//TODO
|
||||
return "", nil
|
||||
}
|
||||
|
||||
_temp_dir :: proc(allocator := context.allocator) -> string {
|
||||
_temp_dir :: proc(allocator: runtime.Allocator) -> (string, Error) {
|
||||
//TODO
|
||||
return ""
|
||||
return "", nil
|
||||
}
|
||||
|
||||
@@ -1,29 +1,29 @@
|
||||
//+private
|
||||
package os2
|
||||
|
||||
import "core:runtime"
|
||||
import win32 "core:sys/windows"
|
||||
|
||||
_create_temp :: proc(dir, pattern: string) -> (Handle, Error) {
|
||||
return 0, nil
|
||||
_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, runtime.Allocator_Error) {
|
||||
n := win32.GetTempPathW(0, nil)
|
||||
if n == 0 {
|
||||
return "", nil
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
+38
-29
@@ -1,66 +1,75 @@
|
||||
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) {
|
||||
found: bool
|
||||
#partial switch ODIN_OS {
|
||||
case .Windows:
|
||||
dir = get_env("LocalAppData") or_return
|
||||
if dir != "" {
|
||||
dir = strings.clone(dir, allocator)
|
||||
dir, found = get_env("LocalAppData")
|
||||
if found {
|
||||
dir = strings.clone_safe(dir, allocator) or_return
|
||||
}
|
||||
case .Darwin:
|
||||
dir = get_env("HOME") or_return
|
||||
if dir != "" {
|
||||
dir = strings.concatenate({dir, "/Library/Caches"}, allocator)
|
||||
dir, found = get_env("HOME")
|
||||
if found {
|
||||
dir = strings.concatenate_safe({dir, "/Library/Caches"}, allocator) or_return
|
||||
}
|
||||
case: // All other UNIX systems
|
||||
dir = get_env("XDG_CACHE_HOME") or_return
|
||||
if dir == "" {
|
||||
dir = get_env("HOME") or_return
|
||||
if dir == "" {
|
||||
dir, found = get_env("XDG_CACHE_HOME")
|
||||
if found {
|
||||
dir, found = get_env("HOME")
|
||||
if !found {
|
||||
return
|
||||
}
|
||||
dir = strings.concatenate({dir, "/.cache"}, allocator)
|
||||
dir = strings.concatenate_safe({dir, "/.cache"}, allocator) or_return
|
||||
}
|
||||
}
|
||||
is_defined = dir != ""
|
||||
if !found || 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) {
|
||||
found: bool
|
||||
#partial switch ODIN_OS {
|
||||
case .Windows:
|
||||
dir = get_env("AppData") or_return
|
||||
if dir != "" {
|
||||
dir = strings.clone(dir, allocator)
|
||||
dir, found = get_env("AppData")
|
||||
if found {
|
||||
dir = strings.clone_safe(dir, allocator) or_return
|
||||
}
|
||||
case .Darwin:
|
||||
dir = get_env("HOME") or_return
|
||||
if dir != "" {
|
||||
dir = strings.concatenate({dir, "/Library/Application Support"}, allocator)
|
||||
dir, found = get_env("HOME")
|
||||
if found {
|
||||
dir = strings.concatenate_safe({dir, "/Library/Application Support"}, allocator) or_return
|
||||
}
|
||||
case: // All other UNIX systems
|
||||
dir = get_env("XDG_CACHE_HOME") or_return
|
||||
if dir == "" {
|
||||
dir = get_env("HOME") or_return
|
||||
if dir == "" {
|
||||
dir, found = get_env("XDG_CACHE_HOME")
|
||||
if !found {
|
||||
dir, found = get_env("HOME")
|
||||
if !found {
|
||||
return
|
||||
}
|
||||
dir = strings.concatenate({dir, "/.config"}, allocator)
|
||||
dir = strings.concatenate_safe({dir, "/.config"}, allocator) or_return
|
||||
}
|
||||
}
|
||||
is_defined = dir != ""
|
||||
if !found || 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"
|
||||
}
|
||||
v := get_env(env) or_return
|
||||
return v, true
|
||||
if v, found := get_env(env); found {
|
||||
return v, nil
|
||||
}
|
||||
return "", .Invalid_Path
|
||||
}
|
||||
|
||||
|
||||
+12
-15
@@ -163,9 +163,6 @@ O_SYNC :: 0x0080
|
||||
O_ASYNC :: 0x0040
|
||||
O_CLOEXEC :: 0x1000000
|
||||
|
||||
SEEK_SET :: 0
|
||||
SEEK_CUR :: 1
|
||||
SEEK_END :: 2
|
||||
SEEK_DATA :: 3
|
||||
SEEK_HOLE :: 4
|
||||
SEEK_MAX :: SEEK_HOLE
|
||||
@@ -279,7 +276,7 @@ foreign libc {
|
||||
@(link_name="__error") __error :: proc() -> ^int ---
|
||||
|
||||
@(link_name="open") _unix_open :: proc(path: cstring, flags: i32, mode: u16) -> Handle ---
|
||||
@(link_name="close") _unix_close :: proc(handle: Handle) ---
|
||||
@(link_name="close") _unix_close :: proc(handle: Handle) -> c.int ---
|
||||
@(link_name="read") _unix_read :: proc(handle: Handle, buffer: rawptr, count: int) -> int ---
|
||||
@(link_name="write") _unix_write :: proc(handle: Handle, buffer: rawptr, count: int) -> int ---
|
||||
@(link_name="lseek") _unix_lseek :: proc(fs: Handle, offset: int, whence: int) -> int ---
|
||||
@@ -298,13 +295,13 @@ foreign libc {
|
||||
|
||||
@(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int ---
|
||||
@(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) ---
|
||||
|
||||
@(link_name="fcntl") _unix_fcntl :: proc(fd: Handle, cmd: c.int, buf: ^byte) -> c.int ---
|
||||
|
||||
@(link_name="__fcntl") _unix__fcntl :: proc(fd: Handle, cmd: c.int, buf: ^byte) -> c.int ---
|
||||
|
||||
@(link_name="rename") _unix_rename :: proc(old: cstring, new: cstring) -> c.int ---
|
||||
@(link_name="remove") _unix_remove :: proc(path: cstring) -> c.int ---
|
||||
|
||||
@(link_name="fchmod") _unix_fchmod :: proc(fildes: Handle, mode: u16) -> c.int ---
|
||||
@(link_name="fchmod") _unix_fchmod :: proc(fd: Handle, mode: u16) -> c.int ---
|
||||
|
||||
@(link_name="malloc") _unix_malloc :: proc(size: int) -> rawptr ---
|
||||
@(link_name="calloc") _unix_calloc :: proc(num, size: int) -> rawptr ---
|
||||
@@ -364,12 +361,12 @@ when ODIN_OS == .Darwin && ODIN_ARCH == .arm64 {
|
||||
return handle, 0
|
||||
}
|
||||
|
||||
fchmod :: proc(fildes: Handle, mode: u16) -> Errno {
|
||||
return cast(Errno)_unix_fchmod(fildes, mode)
|
||||
fchmod :: proc(fd: Handle, mode: u16) -> Errno {
|
||||
return cast(Errno)_unix_fchmod(fd, mode)
|
||||
}
|
||||
|
||||
close :: proc(fd: Handle) {
|
||||
_unix_close(fd)
|
||||
close :: proc(fd: Handle) -> bool {
|
||||
return _unix_close(fd) == 0
|
||||
}
|
||||
|
||||
write :: proc(fd: Handle, data: []u8) -> (int, Errno) {
|
||||
@@ -480,12 +477,12 @@ is_dir :: proc {is_dir_path, is_dir_handle}
|
||||
rename :: proc(old: string, new: string) -> bool {
|
||||
old_cstr := strings.clone_to_cstring(old, context.temp_allocator)
|
||||
new_cstr := strings.clone_to_cstring(new, context.temp_allocator)
|
||||
return _unix_rename(old_cstr, new_cstr) != -1
|
||||
return _unix_rename(old_cstr, new_cstr) != -1
|
||||
}
|
||||
|
||||
remove :: proc(path: string) -> bool {
|
||||
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
return _unix_remove(path_cstr) != -1
|
||||
return _unix_remove(path_cstr) != -1
|
||||
}
|
||||
|
||||
@private
|
||||
@@ -549,7 +546,7 @@ _rewinddir :: proc(dirp: Dir) {
|
||||
_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) {
|
||||
result: ^Dirent
|
||||
rc := _unix_readdir_r(dirp, &entry, &result)
|
||||
|
||||
|
||||
if rc != 0 {
|
||||
err = Errno(get_last_error())
|
||||
return
|
||||
@@ -589,7 +586,7 @@ _readlink :: proc(path: string) -> (string, Errno) {
|
||||
|
||||
absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) {
|
||||
buf : [256]byte
|
||||
res := _unix_fcntl(fd, F_GETPATH, &buf[0])
|
||||
res := _unix__fcntl(fd, F_GETPATH, &buf[0])
|
||||
if res != 0 {
|
||||
return "", Errno(get_last_error())
|
||||
}
|
||||
|
||||
@@ -123,9 +123,6 @@ O_ASYNC :: 0x02000
|
||||
O_CLOEXEC :: 0x80000
|
||||
|
||||
|
||||
SEEK_SET :: 0
|
||||
SEEK_CUR :: 1
|
||||
SEEK_END :: 2
|
||||
SEEK_DATA :: 3
|
||||
SEEK_HOLE :: 4
|
||||
SEEK_MAX :: SEEK_HOLE
|
||||
|
||||
+24
-6
@@ -167,9 +167,6 @@ O_ASYNC :: 0x02000
|
||||
O_CLOEXEC :: 0x80000
|
||||
|
||||
|
||||
SEEK_SET :: 0
|
||||
SEEK_CUR :: 1
|
||||
SEEK_END :: 2
|
||||
SEEK_DATA :: 3
|
||||
SEEK_HOLE :: 4
|
||||
SEEK_MAX :: SEEK_HOLE
|
||||
@@ -418,6 +415,7 @@ foreign libc {
|
||||
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---
|
||||
|
||||
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---
|
||||
@(link_name="putenv") _unix_putenv :: proc(cstring) -> c.int ---
|
||||
@(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
|
||||
|
||||
@(link_name="exit") _unix_exit :: proc(status: c.int) -> ! ---
|
||||
@@ -582,6 +580,11 @@ is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
|
||||
is_file :: proc {is_file_path, is_file_handle}
|
||||
is_dir :: proc {is_dir_path, is_dir_handle}
|
||||
|
||||
exists :: proc(path: string) -> bool {
|
||||
cpath := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
res := _unix_access(cpath, O_RDONLY)
|
||||
return res == 0
|
||||
}
|
||||
|
||||
// NOTE(bill): Uses startup to initialize it
|
||||
|
||||
@@ -767,13 +770,28 @@ heap_free :: proc(ptr: rawptr) {
|
||||
_unix_free(ptr)
|
||||
}
|
||||
|
||||
getenv :: proc(name: string) -> (string, bool) {
|
||||
path_str := strings.clone_to_cstring(name, context.temp_allocator)
|
||||
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||
path_str := strings.clone_to_cstring(key, context.temp_allocator)
|
||||
// NOTE(tetra): Lifetime of 'cstr' is unclear, but _unix_free(cstr) segfaults.
|
||||
cstr := _unix_getenv(path_str)
|
||||
if cstr == nil {
|
||||
return "", false
|
||||
}
|
||||
return string(cstr), true
|
||||
return strings.clone(string(cstr), allocator), true
|
||||
}
|
||||
|
||||
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
|
||||
value, _ = lookup_env(key, allocator)
|
||||
return
|
||||
}
|
||||
|
||||
set_env :: proc(key, value: string) -> Errno {
|
||||
s := strings.concatenate({key, "=", value, "\x00"}, context.temp_allocator)
|
||||
res := _unix_putenv(strings.unsafe_string_to_cstring(s))
|
||||
if res < 0 {
|
||||
return Errno(get_last_error())
|
||||
}
|
||||
return ERROR_NONE
|
||||
}
|
||||
|
||||
get_current_directory :: proc() -> string {
|
||||
|
||||
@@ -125,10 +125,6 @@ O_EXCL :: 0x00800
|
||||
O_NOCTTY :: 0x08000
|
||||
O_CLOEXEC :: 0x10000
|
||||
|
||||
SEEK_SET :: 0
|
||||
SEEK_CUR :: 1
|
||||
SEEK_END :: 2
|
||||
|
||||
RTLD_LAZY :: 0x001
|
||||
RTLD_NOW :: 0x002
|
||||
RTLD_LOCAL :: 0x000
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -20,7 +20,7 @@ full_path_from_name :: proc(name: string, allocator := context.allocator) -> (pa
|
||||
return "", Errno(win32.GetLastError())
|
||||
}
|
||||
if n <= u32(len(buf)) {
|
||||
return win32.utf16_to_utf8(buf[:n], allocator), ERROR_NONE
|
||||
return win32.utf16_to_utf8(buf[:n], allocator) or_else "", ERROR_NONE
|
||||
}
|
||||
resize(&buf, len(buf)*2)
|
||||
}
|
||||
@@ -136,7 +136,7 @@ cleanpath_from_handle :: proc(fd: Handle) -> (string, Errno) {
|
||||
if err != 0 {
|
||||
return "", err
|
||||
}
|
||||
return win32.utf16_to_utf8(buf, context.allocator), err
|
||||
return win32.utf16_to_utf8(buf, context.allocator) or_else "", err
|
||||
}
|
||||
@(private)
|
||||
cleanpath_from_handle_u16 :: proc(fd: Handle) -> ([]u16, Errno) {
|
||||
@@ -157,7 +157,7 @@ cleanpath_from_handle_u16 :: proc(fd: Handle) -> ([]u16, Errno) {
|
||||
cleanpath_from_buf :: proc(buf: []u16) -> string {
|
||||
buf := buf
|
||||
buf = cleanpath_strip_prefix(buf)
|
||||
return win32.utf16_to_utf8(buf, context.allocator)
|
||||
return win32.utf16_to_utf8(buf, context.allocator) or_else ""
|
||||
}
|
||||
|
||||
@(private)
|
||||
|
||||
Reference in New Issue
Block a user