Merge pull request #4020 from odin-lang/bill/os-errno

`os.Error` to replace `os.Errno`
This commit is contained in:
gingerBill
2024-08-04 15:53:18 +01:00
committed by GitHub
49 changed files with 2904 additions and 1808 deletions
+3 -3
View File
@@ -8,9 +8,9 @@ HAS_RAND_BYTES :: true
@(private) @(private)
_rand_bytes :: proc(dst: []byte) { _rand_bytes :: proc(dst: []byte) {
ret := (os.Errno)(win32.BCryptGenRandom(nil, raw_data(dst), u32(len(dst)), win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG)) ret := os.Platform_Error(win32.BCryptGenRandom(nil, raw_data(dst), u32(len(dst)), win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG))
if ret != os.ERROR_NONE { if ret != nil {
switch ret { #partial switch ret {
case os.ERROR_INVALID_HANDLE: case os.ERROR_INVALID_HANDLE:
// The handle to the first parameter is invalid. // The handle to the first parameter is invalid.
// This should not happen here, since we explicitly pass nil to it // This should not happen here, since we explicitly pass nil to it
+3 -3
View File
@@ -38,9 +38,9 @@ iterate_csv_from_stream :: proc(filename: string) {
r.reuse_record_buffer = true // Without it you have to each of the fields within it r.reuse_record_buffer = true // Without it you have to each of the fields within it
defer csv.reader_destroy(&r) defer csv.reader_destroy(&r)
handle, errno := os.open(filename) handle, err := os.open(filename)
if errno != os.ERROR_NONE { if err != nil {
fmt.printfln("Error opening file: %v", filename) fmt.eprintfln("Error opening file: %v", filename)
return return
} }
defer os.close(handle) defer os.close(handle)
+1 -1
View File
@@ -28,7 +28,7 @@ Parse_Error :: struct {
// Provides more granular information than what just a string could hold. // Provides more granular information than what just a string could hold.
Open_File_Error :: struct { Open_File_Error :: struct {
filename: string, filename: string,
errno: os.Errno, errno: os.Error,
mode: int, mode: int,
perms: int, perms: int,
} }
+2 -2
View File
@@ -254,8 +254,8 @@ parse_and_set_pointer_by_named_type :: proc(ptr: rawptr, str: string, data_type:
} }
handle, errno := os.open(str, mode, perms) handle, errno := os.open(str, mode, perms)
if errno != 0 { if errno != nil {
// NOTE(Feoramund): os.Errno is system-dependent, and there's // NOTE(Feoramund): os.Error is system-dependent, and there's
// currently no good way to translate them all into strings. // currently no good way to translate them all into strings.
// //
// The upcoming `os2` package will hopefully solve this. // The upcoming `os2` package will hopefully solve this.
+1 -1
View File
@@ -27,7 +27,7 @@ which :: proc{
which_file :: proc(path: string) -> Which_File_Type { which_file :: proc(path: string) -> Which_File_Type {
f, err := os.open(path) f, err := os.open(path)
if err != 0 { if err != nil {
return .Unknown return .Unknown
} }
header: [128]byte header: [128]byte
+1 -1
View File
@@ -213,7 +213,7 @@ write_image_as_ppm :: proc(filename: string, image: ^image.Image) -> (success: b
} }
fd, err := open(filename, flags, mode) fd, err := open(filename, flags, mode)
if err != 0 { if err != nil {
return false return false
} }
defer close(fd) defer close(fd)
+1 -1
View File
@@ -450,7 +450,7 @@ when false {
} }
fd, fderr := open(filename, flags, mode) fd, fderr := open(filename, flags, mode)
if fderr != 0 { if fderr != nil {
return .Cannot_Open_File return .Cannot_Open_File
} }
defer close(fd) defer close(fd)
+2 -2
View File
@@ -24,7 +24,7 @@ map_file :: proc{
map_file_from_path :: proc(filename: string, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) { map_file_from_path :: proc(filename: string, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) {
fd, err := os.open(filename, os.O_RDWR) fd, err := os.open(filename, os.O_RDWR)
if err != 0 { if err != nil {
return nil, .Open_Failure return nil, .Open_Failure
} }
defer os.close(fd) defer os.close(fd)
@@ -34,7 +34,7 @@ map_file_from_path :: proc(filename: string, flags: Map_File_Flags) -> (data: []
map_file_from_file_descriptor :: proc(fd: uintptr, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) { map_file_from_file_descriptor :: proc(fd: uintptr, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) {
size, os_err := os.file_size(os.Handle(fd)) size, os_err := os.file_size(os.Handle(fd))
if os_err != 0 { if os_err != nil {
return nil, .Stat_Failure return nil, .Stat_Failure
} }
if size < 0 { if size < 0 {
+32 -32
View File
@@ -53,9 +53,9 @@ _create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (so
unreachable() unreachable()
} }
sock, ok := os.socket(c_family, c_type, c_protocol) sock, sock_err := os.socket(c_family, c_type, c_protocol)
if ok != os.ERROR_NONE { if sock_err != nil {
err = Create_Socket_Error(ok) err = Create_Socket_Error(os.is_platform_error(sock_err) or_else -1)
return return
} }
@@ -84,8 +84,8 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
sockaddr := _endpoint_to_sockaddr(endpoint) sockaddr := _endpoint_to_sockaddr(endpoint)
res := os.connect(os.Socket(skt), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len)) res := os.connect(os.Socket(skt), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len))
if res != os.ERROR_NONE { if res != nil {
err = Dial_Error(res) err = Dial_Error(os.is_platform_error(res) or_else -1)
return return
} }
@@ -100,11 +100,11 @@ _bind :: proc(skt: Any_Socket, ep: Endpoint) -> (err: Network_Error) {
sockaddr := _endpoint_to_sockaddr(ep) sockaddr := _endpoint_to_sockaddr(ep)
s := any_socket_to_socket(skt) s := any_socket_to_socket(skt)
res := os.bind(os.Socket(s), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len)) res := os.bind(os.Socket(s), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len))
if res != os.ERROR_NONE { if res != nil {
if res == os.EACCES && ep.port <= MAX_PRIVILEGED_PORT { if res == os.EACCES && ep.port <= MAX_PRIVILEGED_PORT {
err = .Privileged_Port_Without_Root err = .Privileged_Port_Without_Root
} else { } else {
err = Bind_Error(res) err = Bind_Error(os.is_platform_error(res) or_else -1)
} }
} }
return return
@@ -128,8 +128,8 @@ _listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (skt: TCP_
bind(sock, interface_endpoint) or_return bind(sock, interface_endpoint) or_return
res := os.listen(os.Socket(skt), backlog) res := os.listen(os.Socket(skt), backlog)
if res != os.ERROR_NONE { if res != nil {
err = Listen_Error(res) err = Listen_Error(os.is_platform_error(res) or_else -1)
return return
} }
@@ -141,9 +141,9 @@ _accept_tcp :: proc(sock: TCP_Socket, options := default_tcp_options) -> (client
sockaddr: os.SOCKADDR_STORAGE_LH sockaddr: os.SOCKADDR_STORAGE_LH
sockaddrlen := c.int(size_of(sockaddr)) sockaddrlen := c.int(size_of(sockaddr))
client_sock, ok := os.accept(os.Socket(sock), cast(^os.SOCKADDR) &sockaddr, &sockaddrlen) client_sock, client_sock_err := os.accept(os.Socket(sock), cast(^os.SOCKADDR) &sockaddr, &sockaddrlen)
if ok != os.ERROR_NONE { if client_sock_err != nil {
err = Accept_Error(ok) err = Accept_Error(os.is_platform_error(client_sock_err) or_else -1)
return return
} }
client = TCP_Socket(client_sock) client = TCP_Socket(client_sock)
@@ -162,9 +162,9 @@ _recv_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_read: int, err: Networ
if len(buf) <= 0 { if len(buf) <= 0 {
return return
} }
res, ok := os.recv(os.Socket(skt), buf, 0) res, res_err := os.recv(os.Socket(skt), buf, 0)
if ok != os.ERROR_NONE { if res_err != nil {
err = TCP_Recv_Error(ok) err = TCP_Recv_Error(os.is_platform_error(res_err) or_else -1)
return return
} }
return int(res), nil return int(res), nil
@@ -178,9 +178,9 @@ _recv_udp :: proc(skt: UDP_Socket, buf: []byte) -> (bytes_read: int, remote_endp
from: os.SOCKADDR_STORAGE_LH from: os.SOCKADDR_STORAGE_LH
fromsize := c.int(size_of(from)) fromsize := c.int(size_of(from))
res, ok := os.recvfrom(os.Socket(skt), buf, 0, cast(^os.SOCKADDR) &from, &fromsize) res, res_err := os.recvfrom(os.Socket(skt), buf, 0, cast(^os.SOCKADDR) &from, &fromsize)
if ok != os.ERROR_NONE { if res_err != nil {
err = UDP_Recv_Error(ok) err = UDP_Recv_Error(os.is_platform_error(res_err) or_else -1)
return return
} }
@@ -194,9 +194,9 @@ _send_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_written: int, err: Net
for bytes_written < len(buf) { for bytes_written < len(buf) {
limit := min(int(max(i32)), len(buf) - bytes_written) limit := min(int(max(i32)), len(buf) - bytes_written)
remaining := buf[bytes_written:][:limit] remaining := buf[bytes_written:][:limit]
res, ok := os.send(os.Socket(skt), remaining, 0) res, res_err := os.send(os.Socket(skt), remaining, 0)
if ok != os.ERROR_NONE { if res_err != nil {
err = TCP_Send_Error(ok) err = TCP_Send_Error(os.is_platform_error(res_err) or_else -1)
return return
} }
bytes_written += int(res) bytes_written += int(res)
@@ -210,9 +210,9 @@ _send_udp :: proc(skt: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written:
for bytes_written < len(buf) { for bytes_written < len(buf) {
limit := min(1<<31, len(buf) - bytes_written) limit := min(1<<31, len(buf) - bytes_written)
remaining := buf[bytes_written:][:limit] remaining := buf[bytes_written:][:limit]
res, ok := os.sendto(os.Socket(skt), remaining, 0, cast(^os.SOCKADDR)&toaddr, i32(toaddr.len)) res, res_err := os.sendto(os.Socket(skt), remaining, 0, cast(^os.SOCKADDR)&toaddr, i32(toaddr.len))
if ok != os.ERROR_NONE { if res_err != nil {
err = UDP_Send_Error(ok) err = UDP_Send_Error(os.is_platform_error(res_err) or_else -1)
return return
} }
bytes_written += int(res) bytes_written += int(res)
@@ -224,8 +224,8 @@ _send_udp :: proc(skt: UDP_Socket, buf: []byte, to: Endpoint) -> (bytes_written:
_shutdown :: proc(skt: Any_Socket, manner: Shutdown_Manner) -> (err: Network_Error) { _shutdown :: proc(skt: Any_Socket, manner: Shutdown_Manner) -> (err: Network_Error) {
s := any_socket_to_socket(skt) s := any_socket_to_socket(skt)
res := os.shutdown(os.Socket(s), int(manner)) res := os.shutdown(os.Socket(s), int(manner))
if res != os.ERROR_NONE { if res != nil {
return Shutdown_Error(res) return Shutdown_Error(os.is_platform_error(res) or_else -1)
} }
return return
} }
@@ -302,8 +302,8 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca
skt := any_socket_to_socket(s) skt := any_socket_to_socket(s)
res := os.setsockopt(os.Socket(skt), int(level), int(option), ptr, len) res := os.setsockopt(os.Socket(skt), int(level), int(option), ptr, len)
if res != os.ERROR_NONE { if res != nil {
return Socket_Option_Error(res) return Socket_Option_Error(os.is_platform_error(res) or_else -1)
} }
return nil return nil
@@ -314,8 +314,8 @@ _set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Network_E
socket := any_socket_to_socket(socket) socket := any_socket_to_socket(socket)
flags, getfl_err := os.fcntl(int(socket), os.F_GETFL, 0) flags, getfl_err := os.fcntl(int(socket), os.F_GETFL, 0)
if getfl_err != os.ERROR_NONE { if getfl_err != nil {
return Set_Blocking_Error(getfl_err) return Set_Blocking_Error(os.is_platform_error(getfl_err) or_else -1)
} }
if should_block { if should_block {
@@ -325,8 +325,8 @@ _set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Network_E
} }
_, setfl_err := os.fcntl(int(socket), os.F_SETFL, flags) _, setfl_err := os.fcntl(int(socket), os.F_SETFL, flags)
if setfl_err != os.ERROR_NONE { if setfl_err != nil {
return Set_Blocking_Error(setfl_err) return Set_Blocking_Error(os.is_platform_error(setfl_err) or_else -1)
} }
return nil return nil
+9 -18
View File
@@ -3,21 +3,12 @@ package os
import "core:strings" import "core:strings"
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) { @(require_results)
dirp: Dir read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Error) {
dirp, err = _fdopendir(fd) dirp := _fdopendir(fd) or_return
if err != ERROR_NONE {
return
}
defer _closedir(dirp) defer _closedir(dirp)
dirpath: string dirpath := absolute_path_from_handle(fd) or_return
dirpath, err = absolute_path_from_handle(fd)
if err != ERROR_NONE {
return
}
defer delete(dirpath) defer delete(dirpath)
n := n n := n
@@ -27,8 +18,8 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
size = 100 size = 100
} }
dfi := make([dynamic]File_Info, 0, size, allocator) dfi := make([dynamic]File_Info, 0, size, allocator) or_return
defer if err != ERROR_NONE { defer if err != nil {
for fi_ in dfi { for fi_ in dfi {
file_info_delete(fi_, allocator) file_info_delete(fi_, allocator)
} }
@@ -39,7 +30,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
entry: Dirent entry: Dirent
end_of_stream: bool end_of_stream: bool
entry, err, end_of_stream = _readdir(dirp) entry, err, end_of_stream = _readdir(dirp)
if err != ERROR_NONE { if err != nil {
return return
} else if end_of_stream { } else if end_of_stream {
break break
@@ -56,7 +47,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
s: OS_Stat s: OS_Stat
s, err = _lstat(fullpath) s, err = _lstat(fullpath)
if err != ERROR_NONE { if err != nil {
delete(fullpath, allocator) delete(fullpath, allocator)
return return
} }
@@ -67,5 +58,5 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
append(&dfi, fi_) append(&dfi, fi_)
} }
return dfi[:], ERROR_NONE return dfi[:], nil
} }
+11 -10
View File
@@ -4,7 +4,9 @@ import win32 "core:sys/windows"
import "core:strings" import "core:strings"
import "base:runtime" import "base:runtime"
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) { @(require_results)
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Error) {
@(require_results)
find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW) -> (fi: File_Info) { find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW) -> (fi: File_Info) {
// Ignore "." and ".." // Ignore "." and ".."
if d.cFileName[0] == '.' && d.cFileName[1] == 0 { if d.cFileName[0] == '.' && d.cFileName[1] == 0 {
@@ -57,7 +59,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
dir_fi, _ := file_info_from_get_file_information_by_handle("", h) dir_fi, _ := file_info_from_get_file_information_by_handle("", h)
if !dir_fi.is_dir { if !dir_fi.is_dir {
return nil, ERROR_FILE_IS_NOT_DIR return nil, .Not_Dir
} }
n := n n := n
@@ -68,15 +70,14 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
} }
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
wpath: []u16 wpath := cleanpath_from_handle_u16(fd, context.temp_allocator) or_return
wpath, err = cleanpath_from_handle_u16(fd, context.temp_allocator) if len(wpath) == 0 {
if len(wpath) == 0 || err != ERROR_NONE {
return return
} }
dfi := make([dynamic]File_Info, 0, size) dfi := make([dynamic]File_Info, 0, size) or_return
wpath_search := make([]u16, len(wpath)+3, context.temp_allocator) wpath_search := make([]u16, len(wpath)+3, context.temp_allocator) or_return
copy(wpath_search, wpath) copy(wpath_search, wpath)
wpath_search[len(wpath)+0] = '\\' wpath_search[len(wpath)+0] = '\\'
wpath_search[len(wpath)+1] = '*' wpath_search[len(wpath)+1] = '*'
@@ -88,7 +89,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
find_data := &win32.WIN32_FIND_DATAW{} find_data := &win32.WIN32_FIND_DATAW{}
find_handle := win32.FindFirstFileW(raw_data(wpath_search), find_data) find_handle := win32.FindFirstFileW(raw_data(wpath_search), find_data)
if find_handle == win32.INVALID_HANDLE_VALUE { if find_handle == win32.INVALID_HANDLE_VALUE {
err = Errno(win32.GetLastError()) err = get_last_error()
return dfi[:], err return dfi[:], err
} }
defer win32.FindClose(find_handle) defer win32.FindClose(find_handle)
@@ -101,7 +102,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
} }
if !win32.FindNextFileW(find_handle, find_data) { if !win32.FindNextFileW(find_handle, find_data) {
e := Errno(win32.GetLastError()) e := get_last_error()
if e == ERROR_NO_MORE_FILES { if e == ERROR_NO_MORE_FILES {
break break
} }
@@ -109,5 +110,5 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
} }
} }
return dfi[:], ERROR_NONE return dfi[:], nil
} }
+19 -19
View File
@@ -7,27 +7,22 @@ import "base:runtime"
// If the variable is found in the environment the value (which can be empty) is returned and the boolean is true // If the variable is found in the environment the value (which can be empty) is returned and the boolean is true
// Otherwise the returned value will be empty and the boolean will be false // Otherwise the returned value will be empty and the boolean will be false
// NOTE: the value will be allocated with the supplied allocator // NOTE: the value will be allocated with the supplied allocator
@(require_results)
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
if key == "" { if key == "" {
return return
} }
wkey := win32.utf8_to_wstring(key) wkey := win32.utf8_to_wstring(key)
n := win32.GetEnvironmentVariableW(wkey, nil, 0) n := win32.GetEnvironmentVariableW(wkey, nil, 0)
if n == 0 { if n == 0 && get_last_error() == ERROR_ENVVAR_NOT_FOUND {
err := win32.GetLastError() return "", false
if err == u32(ERROR_ENVVAR_NOT_FOUND) {
return "", false
}
} }
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
b := make([dynamic]u16, n, context.temp_allocator) b, _ := make([dynamic]u16, n, context.temp_allocator)
n = win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b))) n = win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b)))
if n == 0 { if n == 0 && get_last_error() == ERROR_ENVVAR_NOT_FOUND {
err := win32.GetLastError() return "", false
if err == u32(ERROR_ENVVAR_NOT_FOUND) {
return "", false
}
} }
value, _ = win32.utf16_to_utf8(b[:n], allocator) value, _ = win32.utf16_to_utf8(b[:n], allocator)
found = true found = true
@@ -39,41 +34,46 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
// It returns the value, which will be empty if the variable is not present // It returns the value, which will be empty if the variable is not present
// To distinguish between an empty value and an unset value, use lookup_env // To distinguish between an empty value and an unset value, use lookup_env
// NOTE: the value will be allocated with the supplied allocator // NOTE: the value will be allocated with the supplied allocator
@(require_results)
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) { get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
value, _ = lookup_env(key, allocator) value, _ = lookup_env(key, allocator)
return return
} }
// set_env sets the value of the environment variable named by the key // set_env sets the value of the environment variable named by the key
set_env :: proc(key, value: string) -> Errno { set_env :: proc(key, value: string) -> Error {
k := win32.utf8_to_wstring(key) k := win32.utf8_to_wstring(key)
v := win32.utf8_to_wstring(value) v := win32.utf8_to_wstring(value)
if !win32.SetEnvironmentVariableW(k, v) { if !win32.SetEnvironmentVariableW(k, v) {
return Errno(win32.GetLastError()) return get_last_error()
} }
return 0 return nil
} }
// unset_env unsets a single environment variable // unset_env unsets a single environment variable
unset_env :: proc(key: string) -> Errno { unset_env :: proc(key: string) -> Error {
k := win32.utf8_to_wstring(key) k := win32.utf8_to_wstring(key)
if !win32.SetEnvironmentVariableW(k, nil) { if !win32.SetEnvironmentVariableW(k, nil) {
return Errno(win32.GetLastError()) return get_last_error()
} }
return 0 return nil
} }
// environ returns a copy of strings representing the environment, in the form "key=value" // environ returns a copy of strings representing the environment, in the form "key=value"
// NOTE: the slice of strings and the strings with be allocated using the supplied allocator // NOTE: the slice of strings and the strings with be allocated using the supplied allocator
@(require_results)
environ :: proc(allocator := context.allocator) -> []string { environ :: proc(allocator := context.allocator) -> []string {
envs := cast([^]win32.WCHAR)(win32.GetEnvironmentStringsW()) envs := ([^]win32.WCHAR)(win32.GetEnvironmentStringsW())
if envs == nil { if envs == nil {
return nil return nil
} }
defer win32.FreeEnvironmentStringsW(envs) defer win32.FreeEnvironmentStringsW(envs)
r := make([dynamic]string, 0, 50, allocator) r, err := make([dynamic]string, 0, 50, allocator)
if err != nil {
return nil
}
for from, i := 0, 0; true; i += 1 { for from, i := 0, 0; true; i += 1 {
if c := envs[i]; c == 0 { if c := envs[i]; c == 0 {
if i <= from { if i <= from {
+322
View File
@@ -0,0 +1,322 @@
package os
import "base:intrinsics"
import "base:runtime"
import "core:io"
Platform_Error :: _Platform_Error
#assert(size_of(Platform_Error) <= 4)
#assert(intrinsics.type_has_nil(Platform_Error))
General_Error :: enum u32 {
None,
Permission_Denied,
Exist,
Not_Exist,
Closed,
Timeout,
Broken_Pipe,
// Indicates that an attempt to retrieve a file's size was made, but the
// file doesn't have a size.
No_Size,
Invalid_File,
Invalid_Dir,
Invalid_Path,
Invalid_Callback,
Pattern_Has_Separator,
Unsupported,
File_Is_Pipe,
Not_Dir,
}
Errno :: Error // alias for legacy use
Error :: union #shared_nil {
General_Error,
io.Error,
runtime.Allocator_Error,
Platform_Error,
}
#assert(size_of(Error) == 8)
ERROR_NONE :: Error{}
ERROR_EOF :: io.Error.EOF
@(require_results)
is_platform_error :: proc "contextless" (ferr: Error) -> (err: i32, ok: bool) {
v := ferr.(Platform_Error) or_else {}
return i32(v), i32(v) != 0
}
@(require_results)
error_string :: proc "contextless" (ferr: Error) -> string {
if ferr == nil {
return ""
}
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 .Broken_Pipe: return "Broken pipe"
case .No_Size: return "file has no definite size"
case .Invalid_File: return "invalid file"
case .Invalid_Dir: return "invalid directory"
case .Invalid_Path: return "invalid path"
case .Invalid_Callback: return "invalid callback"
case .Unsupported: return "unsupported"
case .Pattern_Has_Separator: return "pattern has separator"
case .File_Is_Pipe: return "file is pipe"
case .Not_Dir: return "file is not directory"
}
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(e)
}
return "unknown error"
}
print_error :: proc(f: Handle, ferr: Error, msg: string) -> (n: int, err: Error) {
err_str := error_string(ferr)
// msg + ": " + err_str + '\n'
length := len(msg) + 2 + len(err_str) + 1
buf_ := intrinsics.alloca(length, 1)
buf := buf_[:length]
copy(buf, msg)
buf[len(msg)] = ':'
buf[len(msg) + 1] = ' '
copy(buf[len(msg) + 2:], err_str)
buf[length - 1] = '\n'
return write(f, buf)
}
@(require_results, private)
_error_string :: proc "contextless" (e: Platform_Error) -> string where intrinsics.type_is_enum(Platform_Error) {
if e == nil {
return ""
}
when ODIN_OS == .Darwin {
if s := string(_darwin_string_error(i32(e))); s != "" {
return s
}
}
when ODIN_OS != .Linux {
@(require_results)
binary_search :: proc "contextless" (array: $A/[]$T, key: T) -> (index: int, found: bool) #no_bounds_check {
n := len(array)
left, right := 0, n
for left < right {
mid := int(uint(left+right) >> 1)
if array[mid] < key {
left = mid+1
} else {
// equal or greater
right = mid
}
}
return left, left < n && array[left] == key
}
err := runtime.Type_Info_Enum_Value(e)
ti := &runtime.type_info_base(type_info_of(Platform_Error)).variant.(runtime.Type_Info_Enum)
if idx, ok := binary_search(ti.values, err); ok {
return ti.names[idx]
}
} else {
@(rodata, static)
pe_strings := [Platform_Error]string{
.NONE = "",
.EPERM = "Operation not permitted",
.ENOENT = "No such file or directory",
.ESRCH = "No such process",
.EINTR = "Interrupted system call",
.EIO = "Input/output error",
.ENXIO = "No such device or address",
.E2BIG = "Argument list too long",
.ENOEXEC = "Exec format error",
.EBADF = "Bad file descriptor",
.ECHILD = "No child processes",
.EAGAIN = "Resource temporarily unavailable",
.ENOMEM = "Cannot allocate memory",
.EACCES = "Permission denied",
.EFAULT = "Bad address",
.ENOTBLK = "Block device required",
.EBUSY = "Device or resource busy",
.EEXIST = "File exists",
.EXDEV = "Invalid cross-device link",
.ENODEV = "No such device",
.ENOTDIR = "Not a directory",
.EISDIR = "Is a directory",
.EINVAL = "Invalid argument",
.ENFILE = "Too many open files in system",
.EMFILE = "Too many open files",
.ENOTTY = "Inappropriate ioctl for device",
.ETXTBSY = "Text file busy",
.EFBIG = "File too large",
.ENOSPC = "No space left on device",
.ESPIPE = "Illegal seek",
.EROFS = "Read-only file system",
.EMLINK = "Too many links",
.EPIPE = "Broken pipe",
.EDOM = "Numerical argument out of domain",
.ERANGE = "Numerical result out of range",
.EDEADLK = "Resource deadlock avoided",
.ENAMETOOLONG = "File name too long",
.ENOLCK = "No locks available",
.ENOSYS = "Function not implemented",
.ENOTEMPTY = "Directory not empty",
.ELOOP = "Too many levels of symbolic links",
.EUNKNOWN_41 = "Unknown Error (41)",
.ENOMSG = "No message of desired type",
.EIDRM = "Identifier removed",
.ECHRNG = "Channel number out of range",
.EL2NSYNC = "Level 2 not synchronized",
.EL3HLT = "Level 3 halted",
.EL3RST = "Level 3 reset",
.ELNRNG = "Link number out of range",
.EUNATCH = "Protocol driver not attached",
.ENOCSI = "No CSI structure available",
.EL2HLT = "Level 2 halted",
.EBADE = "Invalid exchange",
.EBADR = "Invalid request descriptor",
.EXFULL = "Exchange full",
.ENOANO = "No anode",
.EBADRQC = "Invalid request code",
.EBADSLT = "Invalid slot",
.EUNKNOWN_58 = "Unknown Error (58)",
.EBFONT = "Bad font file format",
.ENOSTR = "Device not a stream",
.ENODATA = "No data available",
.ETIME = "Timer expired",
.ENOSR = "Out of streams resources",
.ENONET = "Machine is not on the network",
.ENOPKG = "Package not installed",
.EREMOTE = "Object is remote",
.ENOLINK = "Link has been severed",
.EADV = "Advertise error",
.ESRMNT = "Srmount error",
.ECOMM = "Communication error on send",
.EPROTO = "Protocol error",
.EMULTIHOP = "Multihop attempted",
.EDOTDOT = "RFS specific error",
.EBADMSG = "Bad message",
.EOVERFLOW = "Value too large for defined data type",
.ENOTUNIQ = "Name not unique on network",
.EBADFD = "File descriptor in bad state",
.EREMCHG = "Remote address changed",
.ELIBACC = "Can not access a needed shared library",
.ELIBBAD = "Accessing a corrupted shared library",
.ELIBSCN = ".lib section in a.out corrupted",
.ELIBMAX = "Attempting to link in too many shared libraries",
.ELIBEXEC = "Cannot exec a shared library directly",
.EILSEQ = "Invalid or incomplete multibyte or wide character",
.ERESTART = "Interrupted system call should be restarted",
.ESTRPIPE = "Streams pipe error",
.EUSERS = "Too many users",
.ENOTSOCK = "Socket operation on non-socket",
.EDESTADDRREQ = "Destination address required",
.EMSGSIZE = "Message too long",
.EPROTOTYPE = "Protocol wrong type for socket",
.ENOPROTOOPT = "Protocol not available",
.EPROTONOSUPPORT = "Protocol not supported",
.ESOCKTNOSUPPORT = "Socket type not supported",
.EOPNOTSUPP = "Operation not supported",
.EPFNOSUPPORT = "Protocol family not supported",
.EAFNOSUPPORT = "Address family not supported by protocol",
.EADDRINUSE = "Address already in use",
.EADDRNOTAVAIL = "Cannot assign requested address",
.ENETDOWN = "Network is down",
.ENETUNREACH = "Network is unreachable",
.ENETRESET = "Network dropped connection on reset",
.ECONNABORTED = "Software caused connection abort",
.ECONNRESET = "Connection reset by peer",
.ENOBUFS = "No buffer space available",
.EISCONN = "Transport endpoint is already connected",
.ENOTCONN = "Transport endpoint is not connected",
.ESHUTDOWN = "Cannot send after transport endpoint shutdown",
.ETOOMANYREFS = "Too many references: cannot splice",
.ETIMEDOUT = "Connection timed out",
.ECONNREFUSED = "Connection refused",
.EHOSTDOWN = "Host is down",
.EHOSTUNREACH = "No route to host",
.EALREADY = "Operation already in progress",
.EINPROGRESS = "Operation now in progress",
.ESTALE = "Stale file handle",
.EUCLEAN = "Structure needs cleaning",
.ENOTNAM = "Not a XENIX named type file",
.ENAVAIL = "No XENIX semaphores available",
.EISNAM = "Is a named type file",
.EREMOTEIO = "Remote I/O error",
.EDQUOT = "Disk quota exceeded",
.ENOMEDIUM = "No medium found",
.EMEDIUMTYPE = "Wrong medium type",
.ECANCELED = "Operation canceled",
.ENOKEY = "Required key not available",
.EKEYEXPIRED = "Key has expired",
.EKEYREVOKED = "Key has been revoked",
.EKEYREJECTED = "Key was rejected by service",
.EOWNERDEAD = "Owner died",
.ENOTRECOVERABLE = "State not recoverable",
.ERFKILL = "Operation not possible due to RF-kill",
.EHWPOISON = "Memory page has hardware error",
}
if Platform_Error.NONE <= e && e <= max(Platform_Error) {
return pe_strings[e]
}
}
return "<unknown platform error>"
}
@(private, require_results)
error_to_io_error :: proc(ferr: Error) -> io.Error {
if ferr == nil {
return .None
}
return ferr.(io.Error) or_else .Unknown
}
+83 -93
View File
@@ -5,13 +5,15 @@ import "base:intrinsics"
import "base:runtime" import "base:runtime"
import "core:unicode/utf16" import "core:unicode/utf16"
@(require_results)
is_path_separator :: proc(c: byte) -> bool { is_path_separator :: proc(c: byte) -> bool {
return c == '/' || c == '\\' return c == '/' || c == '\\'
} }
open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) { @(require_results)
open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Error) {
if len(path) == 0 { if len(path) == 0 {
return INVALID_HANDLE, ERROR_FILE_NOT_FOUND return INVALID_HANDLE, General_Error.Not_Exist
} }
access: u32 access: u32
@@ -52,32 +54,31 @@ open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errn
wide_path := win32.utf8_to_wstring(path) 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)) 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 { if handle != INVALID_HANDLE {
return handle, ERROR_NONE return handle, nil
} }
err := Errno(win32.GetLastError()) return INVALID_HANDLE, get_last_error()
return INVALID_HANDLE, err
} }
close :: proc(fd: Handle) -> Errno { close :: proc(fd: Handle) -> Error {
if !win32.CloseHandle(win32.HANDLE(fd)) { if !win32.CloseHandle(win32.HANDLE(fd)) {
return Errno(win32.GetLastError()) return get_last_error()
} }
return ERROR_NONE return nil
} }
flush :: proc(fd: Handle) -> (err: Errno) { flush :: proc(fd: Handle) -> (err: Error) {
if !win32.FlushFileBuffers(win32.HANDLE(fd)) { if !win32.FlushFileBuffers(win32.HANDLE(fd)) {
err = Errno(win32.GetLastError()) err = get_last_error()
} }
return return
} }
write :: proc(fd: Handle, data: []byte) -> (int, Errno) { write :: proc(fd: Handle, data: []byte) -> (int, Error) {
if len(data) == 0 { if len(data) == 0 {
return 0, ERROR_NONE return 0, nil
} }
single_write_length: win32.DWORD single_write_length: win32.DWORD
@@ -90,25 +91,24 @@ write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
e := win32.WriteFile(win32.HANDLE(fd), &data[total_write], to_write, &single_write_length, nil) e := win32.WriteFile(win32.HANDLE(fd), &data[total_write], to_write, &single_write_length, nil)
if single_write_length <= 0 || !e { if single_write_length <= 0 || !e {
err := Errno(win32.GetLastError()) return int(total_write), get_last_error()
return int(total_write), err
} }
total_write += i64(single_write_length) total_write += i64(single_write_length)
} }
return int(total_write), ERROR_NONE return int(total_write), nil
} }
@(private="file") @(private="file", require_results)
read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) { read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Error) {
if len(b) == 0 { if len(b) == 0 {
return 0, 0 return 0, nil
} }
BUF_SIZE :: 386 BUF_SIZE :: 386
buf16: [BUF_SIZE]u16 buf16: [BUF_SIZE]u16
buf8: [4*BUF_SIZE]u8 buf8: [4*BUF_SIZE]u8
for n < len(b) && err == 0 { for n < len(b) && err == nil {
min_read := max(len(b)/4, 1 if len(b) > 0 else 0) min_read := max(len(b)/4, 1 if len(b) > 0 else 0)
max_read := u32(min(BUF_SIZE, min_read)) max_read := u32(min(BUF_SIZE, min_read))
if max_read == 0 { if max_read == 0 {
@@ -118,7 +118,7 @@ read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) {
single_read_length: u32 single_read_length: u32
ok := win32.ReadConsoleW(handle, &buf16[0], max_read, &single_read_length, nil) ok := win32.ReadConsoleW(handle, &buf16[0], max_read, &single_read_length, nil)
if !ok { if !ok {
err = Errno(win32.GetLastError()) err = get_last_error()
} }
buf8_len := utf16.decode_to_utf8(buf8[:], buf16[:single_read_length]) buf8_len := utf16.decode_to_utf8(buf8[:], buf16[:single_read_length])
@@ -149,9 +149,9 @@ read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) {
return return
} }
read :: proc(fd: Handle, data: []byte) -> (total_read: int, err: Errno) { read :: proc(fd: Handle, data: []byte) -> (total_read: int, err: Error) {
if len(data) == 0 { if len(data) == 0 {
return 0, ERROR_NONE return 0, nil
} }
handle := win32.HANDLE(fd) handle := win32.HANDLE(fd)
@@ -165,7 +165,7 @@ read :: proc(fd: Handle, data: []byte) -> (total_read: int, err: Errno) {
if is_console { if is_console {
total_read, err = read_console(handle, data[total_read:][:to_read]) total_read, err = read_console(handle, data[total_read:][:to_read])
if err != 0 { if err != nil {
return total_read, err return total_read, err
} }
} else { } else {
@@ -175,18 +175,18 @@ read :: proc(fd: Handle, data: []byte) -> (total_read: int, err: Errno) {
// Successful read can mean two things, including EOF, see: // Successful read can mean two things, including EOF, see:
// https://learn.microsoft.com/en-us/windows/win32/fileio/testing-for-the-end-of-a-file // https://learn.microsoft.com/en-us/windows/win32/fileio/testing-for-the-end-of-a-file
if bytes_read == 0 { if bytes_read == 0 {
return 0, ERROR_HANDLE_EOF return 0, .EOF
} else { } else {
return int(bytes_read), ERROR_NONE return int(bytes_read), nil
} }
} else { } else {
return 0, Errno(win32.GetLastError()) return 0, get_last_error()
} }
} }
return total_read, ERROR_NONE return total_read, nil
} }
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
w: u32 w: u32
switch whence { switch whence {
case 0: w = win32.FILE_BEGIN case 0: w = win32.FILE_BEGIN
@@ -197,22 +197,23 @@ seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
lo := i32(offset) lo := i32(offset)
ft := win32.GetFileType(win32.HANDLE(fd)) ft := win32.GetFileType(win32.HANDLE(fd))
if ft == win32.FILE_TYPE_PIPE { if ft == win32.FILE_TYPE_PIPE {
return 0, ERROR_FILE_IS_PIPE return 0, .File_Is_Pipe
} }
dw_ptr := win32.SetFilePointer(win32.HANDLE(fd), lo, &hi, w) dw_ptr := win32.SetFilePointer(win32.HANDLE(fd), lo, &hi, w)
if dw_ptr == win32.INVALID_SET_FILE_POINTER { if dw_ptr == win32.INVALID_SET_FILE_POINTER {
err := Errno(win32.GetLastError()) err := get_last_error()
return 0, err return 0, err
} }
return i64(hi)<<32 + i64(dw_ptr), ERROR_NONE return i64(hi)<<32 + i64(dw_ptr), nil
} }
file_size :: proc(fd: Handle) -> (i64, Errno) { @(require_results)
file_size :: proc(fd: Handle) -> (i64, Error) {
length: win32.LARGE_INTEGER length: win32.LARGE_INTEGER
err: Errno err: Error
if !win32.GetFileSizeEx(win32.HANDLE(fd), &length) { if !win32.GetFileSizeEx(win32.HANDLE(fd), &length) {
err = Errno(win32.GetLastError()) err = get_last_error()
} }
return i64(length), err return i64(length), err
} }
@@ -220,10 +221,9 @@ file_size :: proc(fd: Handle) -> (i64, Errno) {
@(private) @(private)
MAX_RW :: 1<<30 MAX_RW :: 1<<30
ERROR_EOF :: 38
@(private) @(private)
pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) { pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Error) {
buf := data buf := data
if len(buf) > MAX_RW { if len(buf) > MAX_RW {
buf = buf[:MAX_RW] buf = buf[:MAX_RW]
@@ -239,15 +239,15 @@ pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
h := win32.HANDLE(fd) h := win32.HANDLE(fd)
done: win32.DWORD done: win32.DWORD
e: Errno e: Error
if !win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o) { if !win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
e = Errno(win32.GetLastError()) e = get_last_error()
done = 0 done = 0
} }
return int(done), e return int(done), e
} }
@(private) @(private)
pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) { pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Error) {
buf := data buf := data
if len(buf) > MAX_RW { if len(buf) > MAX_RW {
buf = buf[:MAX_RW] buf = buf[:MAX_RW]
@@ -261,9 +261,9 @@ pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
h := win32.HANDLE(fd) h := win32.HANDLE(fd)
done: win32.DWORD done: win32.DWORD
e: Errno e: Error
if !win32.WriteFile(h, raw_data(buf), u32(len(buf)), &done, &o) { if !win32.WriteFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
e = Errno(win32.GetLastError()) e = get_last_error()
done = 0 done = 0
} }
return int(done), e return int(done), e
@@ -279,19 +279,19 @@ on Windows, read_at changes the position of the file cursor, on *nix, it does no
will read from the location twice on *nix, and from two different locations on Windows will read from the location twice on *nix, and from two different locations on Windows
*/ */
read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Errno) { read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
if offset < 0 { if offset < 0 {
return 0, ERROR_NEGATIVE_OFFSET return 0, .Invalid_Offset
} }
b, offset := data, offset b, offset := data, offset
for len(b) > 0 { for len(b) > 0 {
m, e := pread(fd, b, offset) m, e := pread(fd, b, offset)
if e == ERROR_EOF { if e == ERROR_EOF {
err = 0 err = nil
break break
} }
if e != 0 { if e != nil {
err = e err = e
break break
} }
@@ -311,18 +311,14 @@ on Windows, write_at changes the position of the file cursor, on *nix, it does n
will write to the location twice on *nix, and to two different locations on Windows will write to the location twice on *nix, and to two different locations on Windows
*/ */
write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Errno) { write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
if offset < 0 { if offset < 0 {
return 0, ERROR_NEGATIVE_OFFSET return 0, .Invalid_Offset
} }
b, offset := data, offset b, offset := data, offset
for len(b) > 0 { for len(b) > 0 {
m, e := pwrite(fd, b, offset) m := pwrite(fd, b, offset) or_return
if e != 0 {
err = e
break
}
n += m n += m
b = b[m:] b = b[m:]
offset += i64(m) offset += i64(m)
@@ -338,6 +334,7 @@ stdout := get_std_handle(uint(win32.STD_OUTPUT_HANDLE))
stderr := get_std_handle(uint(win32.STD_ERROR_HANDLE)) stderr := get_std_handle(uint(win32.STD_ERROR_HANDLE))
@(require_results)
get_std_handle :: proc "contextless" (h: uint) -> Handle { get_std_handle :: proc "contextless" (h: uint) -> Handle {
fd := win32.GetStdHandle(win32.DWORD(h)) fd := win32.GetStdHandle(win32.DWORD(h))
return Handle(fd) return Handle(fd)
@@ -352,6 +349,7 @@ exists :: proc(path: string) -> bool {
return attribs != win32.INVALID_FILE_ATTRIBUTES return attribs != win32.INVALID_FILE_ATTRIBUTES
} }
@(require_results)
is_file :: proc(path: string) -> bool { is_file :: proc(path: string) -> bool {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
wpath := win32.utf8_to_wstring(path, context.temp_allocator) wpath := win32.utf8_to_wstring(path, context.temp_allocator)
@@ -363,6 +361,7 @@ is_file :: proc(path: string) -> bool {
return false return false
} }
@(require_results)
is_dir :: proc(path: string) -> bool { is_dir :: proc(path: string) -> bool {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
wpath := win32.utf8_to_wstring(path, context.temp_allocator) wpath := win32.utf8_to_wstring(path, context.temp_allocator)
@@ -377,13 +376,14 @@ is_dir :: proc(path: string) -> bool {
// NOTE(tetra): GetCurrentDirectory is not thread safe with SetCurrentDirectory and GetFullPathName // NOTE(tetra): GetCurrentDirectory is not thread safe with SetCurrentDirectory and GetFullPathName
@private cwd_lock := win32.SRWLOCK{} // zero is initialized @private cwd_lock := win32.SRWLOCK{} // zero is initialized
@(require_results)
get_current_directory :: proc(allocator := context.allocator) -> string { get_current_directory :: proc(allocator := context.allocator) -> string {
win32.AcquireSRWLockExclusive(&cwd_lock) win32.AcquireSRWLockExclusive(&cwd_lock)
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
sz_utf16 := win32.GetCurrentDirectoryW(0, nil) sz_utf16 := win32.GetCurrentDirectoryW(0, nil)
dir_buf_wstr := make([]u16, sz_utf16, context.temp_allocator) // the first time, it _includes_ the NUL. dir_buf_wstr, _ := make([]u16, sz_utf16, context.temp_allocator) // the first time, it _includes_ the NUL.
sz_utf16 = win32.GetCurrentDirectoryW(win32.DWORD(len(dir_buf_wstr)), raw_data(dir_buf_wstr)) sz_utf16 = win32.GetCurrentDirectoryW(win32.DWORD(len(dir_buf_wstr)), raw_data(dir_buf_wstr))
assert(int(sz_utf16)+1 == len(dir_buf_wstr)) // the second time, it _excludes_ the NUL. assert(int(sz_utf16)+1 == len(dir_buf_wstr)) // the second time, it _excludes_ the NUL.
@@ -393,14 +393,14 @@ get_current_directory :: proc(allocator := context.allocator) -> string {
return win32.utf16_to_utf8(dir_buf_wstr, allocator) or_else "" return win32.utf16_to_utf8(dir_buf_wstr, allocator) or_else ""
} }
set_current_directory :: proc(path: string) -> (err: Errno) { set_current_directory :: proc(path: string) -> (err: Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
wstr := win32.utf8_to_wstring(path, context.temp_allocator) wstr := win32.utf8_to_wstring(path, context.temp_allocator)
win32.AcquireSRWLockExclusive(&cwd_lock) win32.AcquireSRWLockExclusive(&cwd_lock)
if !win32.SetCurrentDirectoryW(wstr) { if !win32.SetCurrentDirectoryW(wstr) {
err = Errno(win32.GetLastError()) err = get_last_error()
} }
win32.ReleaseSRWLockExclusive(&cwd_lock) win32.ReleaseSRWLockExclusive(&cwd_lock)
@@ -409,31 +409,31 @@ set_current_directory :: proc(path: string) -> (err: Errno) {
} }
change_directory :: set_current_directory change_directory :: set_current_directory
make_directory :: proc(path: string, mode: u32 = 0) -> (err: Errno) { make_directory :: proc(path: string, mode: u32 = 0) -> (err: Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
// Mode is unused on Windows, but is needed on *nix // Mode is unused on Windows, but is needed on *nix
wpath := win32.utf8_to_wstring(path, context.temp_allocator) wpath := win32.utf8_to_wstring(path, context.temp_allocator)
if !win32.CreateDirectoryW(wpath, nil) { if !win32.CreateDirectoryW(wpath, nil) {
err = Errno(win32.GetLastError()) err = get_last_error()
} }
return return
} }
remove_directory :: proc(path: string) -> (err: Errno) { remove_directory :: proc(path: string) -> (err: Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
wpath := win32.utf8_to_wstring(path, context.temp_allocator) wpath := win32.utf8_to_wstring(path, context.temp_allocator)
if !win32.RemoveDirectoryW(wpath) { if !win32.RemoveDirectoryW(wpath) {
err = Errno(win32.GetLastError()) err = get_last_error()
} }
return return
} }
@(private) @(private, require_results)
is_abs :: proc(path: string) -> bool { is_abs :: proc(path: string) -> bool {
if len(path) > 0 && path[0] == '/' { if len(path) > 0 && path[0] == '/' {
return true return true
@@ -449,7 +449,7 @@ is_abs :: proc(path: string) -> bool {
return false return false
} }
@(private) @(private, require_results)
fix_long_path :: proc(path: string) -> string { fix_long_path :: proc(path: string) -> string {
if len(path) < 248 { if len(path) < 248 {
return path return path
@@ -464,7 +464,7 @@ fix_long_path :: proc(path: string) -> string {
prefix :: `\\?` prefix :: `\\?`
path_buf := make([]byte, len(prefix)+len(path)+len(`\`), context.temp_allocator) path_buf, _ := make([]byte, len(prefix)+len(path)+len(`\`), context.temp_allocator)
copy(path_buf, prefix) copy(path_buf, prefix)
n := len(path) n := len(path)
r, w := 0, len(prefix) r, w := 0, len(prefix)
@@ -494,80 +494,69 @@ fix_long_path :: proc(path: string) -> string {
} }
link :: proc(old_name, new_name: string) -> (err: Errno) { link :: proc(old_name, new_name: string) -> (err: Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
n := win32.utf8_to_wstring(fix_long_path(new_name)) n := win32.utf8_to_wstring(fix_long_path(new_name))
o := win32.utf8_to_wstring(fix_long_path(old_name)) o := win32.utf8_to_wstring(fix_long_path(old_name))
return Errno(win32.CreateHardLinkW(n, o, nil)) return Platform_Error(win32.CreateHardLinkW(n, o, nil))
} }
unlink :: proc(path: string) -> (err: Errno) { unlink :: proc(path: string) -> (err: Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
wpath := win32.utf8_to_wstring(path, context.temp_allocator) wpath := win32.utf8_to_wstring(path, context.temp_allocator)
if !win32.DeleteFileW(wpath) { if !win32.DeleteFileW(wpath) {
err = Errno(win32.GetLastError()) err = get_last_error()
} }
return return
} }
rename :: proc(old_path, new_path: string) -> (err: Errno) { rename :: proc(old_path, new_path: string) -> (err: Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
from := win32.utf8_to_wstring(old_path, context.temp_allocator) from := win32.utf8_to_wstring(old_path, context.temp_allocator)
to := win32.utf8_to_wstring(new_path, context.temp_allocator) to := win32.utf8_to_wstring(new_path, context.temp_allocator)
if !win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING) { if !win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING) {
err = Errno(win32.GetLastError()) err = get_last_error()
} }
return return
} }
ftruncate :: proc(fd: Handle, length: i64) -> (err: Errno) { ftruncate :: proc(fd: Handle, length: i64) -> (err: Error) {
curr_off, e := seek(fd, 0, 1) curr_off := seek(fd, 0, 1) or_return
if e != 0 {
return e
}
defer seek(fd, curr_off, 0) defer seek(fd, curr_off, 0)
_, e = seek(fd, length, 0) _= seek(fd, length, 0) or_return
if e != 0 {
return e
}
ok := win32.SetEndOfFile(win32.HANDLE(fd)) ok := win32.SetEndOfFile(win32.HANDLE(fd))
if !ok { if !ok {
return Errno(win32.GetLastError()) return get_last_error()
} }
return ERROR_NONE return nil
} }
truncate :: proc(path: string, length: i64) -> (err: Errno) { truncate :: proc(path: string, length: i64) -> (err: Error) {
fd: Handle fd := open(path, O_WRONLY|O_CREATE, 0o666) or_return
fd, err = open(path, O_WRONLY|O_CREATE, 0o666)
if err != 0 {
return
}
defer close(fd) defer close(fd)
err = ftruncate(fd, length) return ftruncate(fd, length)
return
} }
remove :: proc(name: string) -> Errno { remove :: proc(name: string) -> Error {
p := win32.utf8_to_wstring(fix_long_path(name)) p := win32.utf8_to_wstring(fix_long_path(name))
err, err1: win32.DWORD err, err1: win32.DWORD
if !win32.DeleteFileW(p) { if !win32.DeleteFileW(p) {
err = win32.GetLastError() err = win32.GetLastError()
} }
if err == 0 { if err == 0 {
return 0 return nil
} }
if !win32.RemoveDirectoryW(p) { if !win32.RemoveDirectoryW(p) {
err1 = win32.GetLastError() err1 = win32.GetLastError()
} }
if err1 == 0 { if err1 == 0 {
return 0 return nil
} }
if err != err1 { if err != err1 {
@@ -588,16 +577,17 @@ remove :: proc(name: string) -> Errno {
} }
} }
return Errno(err) return Platform_Error(err)
} }
pipe :: proc() -> (r, w: Handle, err: Errno) { @(require_results)
pipe :: proc() -> (r, w: Handle, err: Error) {
sa: win32.SECURITY_ATTRIBUTES sa: win32.SECURITY_ATTRIBUTES
sa.nLength = size_of(win32.SECURITY_ATTRIBUTES) sa.nLength = size_of(win32.SECURITY_ATTRIBUTES)
sa.bInheritHandle = true sa.bInheritHandle = true
if !win32.CreatePipe((^win32.HANDLE)(&r), (^win32.HANDLE)(&w), &sa, 0) { if !win32.CreatePipe((^win32.HANDLE)(&r), (^win32.HANDLE)(&w), &sa, 0) {
err = Errno(win32.GetLastError()) err = get_last_error()
} }
return return
} }
+98 -66
View File
@@ -1,6 +1,8 @@
package os package os
import "base:intrinsics"
import "base:runtime" import "base:runtime"
import "core:io"
import "core:strconv" import "core:strconv"
import "core:unicode/utf8" import "core:unicode/utf8"
@@ -13,15 +15,15 @@ SEEK_SET :: 0
SEEK_CUR :: 1 SEEK_CUR :: 1
SEEK_END :: 2 SEEK_END :: 2
write_string :: proc(fd: Handle, str: string) -> (int, Errno) { write_string :: proc(fd: Handle, str: string) -> (int, Error) {
return write(fd, transmute([]byte)str) return write(fd, transmute([]byte)str)
} }
write_byte :: proc(fd: Handle, b: byte) -> (int, Errno) { write_byte :: proc(fd: Handle, b: byte) -> (int, Error) {
return write(fd, []byte{b}) return write(fd, []byte{b})
} }
write_rune :: proc(fd: Handle, r: rune) -> (int, Errno) { write_rune :: proc(fd: Handle, r: rune) -> (int, Error) {
if r < utf8.RUNE_SELF { if r < utf8.RUNE_SELF {
return write_byte(fd, byte(r)) return write_byte(fd, byte(r))
} }
@@ -30,105 +32,94 @@ write_rune :: proc(fd: Handle, r: rune) -> (int, Errno) {
return write(fd, b[:n]) return write(fd, b[:n])
} }
write_encoded_rune :: proc(fd: Handle, r: rune) { write_encoded_rune :: proc(f: Handle, r: rune) -> (n: int, err: Error) {
write_byte(fd, '\'') wrap :: proc(m: int, merr: Error, n: ^int, err: ^Error) -> bool {
n^ += m
if merr != nil {
err^ = merr
return true
}
return false
}
if wrap(write_byte(f, '\''), &n, &err) { return }
switch r { switch r {
case '\a': write_string(fd, "\\a") case '\a': if wrap(write_string(f, "\\a"), &n, &err) { return }
case '\b': write_string(fd, "\\b") case '\b': if wrap(write_string(f, "\\b"), &n, &err) { return }
case '\e': write_string(fd, "\\e") case '\e': if wrap(write_string(f, "\\e"), &n, &err) { return }
case '\f': write_string(fd, "\\f") case '\f': if wrap(write_string(f, "\\f"), &n, &err) { return }
case '\n': write_string(fd, "\\n") case '\n': if wrap(write_string(f, "\\n"), &n, &err) { return }
case '\r': write_string(fd, "\\r") case '\r': if wrap(write_string(f, "\\r"), &n, &err) { return }
case '\t': write_string(fd, "\\t") case '\t': if wrap(write_string(f, "\\t"), &n, &err) { return }
case '\v': write_string(fd, "\\v") case '\v': if wrap(write_string(f, "\\v"), &n, &err) { return }
case: case:
if r < 32 { if r < 32 {
write_string(fd, "\\x") if wrap(write_string(f, "\\x"), &n, &err) { return }
b: [2]byte b: [2]byte
s := strconv.append_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil) s := strconv.append_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil)
switch len(s) { switch len(s) {
case 0: write_string(fd, "00") case 0: if wrap(write_string(f, "00"), &n, &err) { return }
case 1: write_rune(fd, '0') case 1: if wrap(write_rune(f, '0'), &n, &err) { return }
case 2: write_string(fd, s) case 2: if wrap(write_string(f, s), &n, &err) { return }
} }
} else { } else {
write_rune(fd, r) if wrap(write_rune(f, r), &n, &err) { return }
} }
} }
write_byte(fd, '\'') _ = wrap(write_byte(f, '\''), &n, &err)
return
} }
read_at_least :: proc(fd: Handle, buf: []byte, min: int) -> (n: int, err: Errno) { read_at_least :: proc(fd: Handle, buf: []byte, min: int) -> (n: int, err: Error) {
if len(buf) < min { if len(buf) < min {
return 0, -1 return 0, io.Error.Short_Buffer
} }
nn := max(int) nn := max(int)
for nn > 0 && n < min && err == 0 { for nn > 0 && n < min && err == nil {
nn, err = read(fd, buf[n:]) nn, err = read(fd, buf[n:])
n += nn n += nn
} }
if n >= min { if n >= min {
err = 0 err = nil
} }
return return
} }
read_full :: proc(fd: Handle, buf: []byte) -> (n: int, err: Errno) { read_full :: proc(fd: Handle, buf: []byte) -> (n: int, err: Error) {
return read_at_least(fd, buf, len(buf)) return read_at_least(fd, buf, len(buf))
} }
@(require_results)
file_size_from_path :: proc(path: string) -> i64 { file_size_from_path :: proc(path: string) -> i64 {
fd, err := open(path, O_RDONLY, 0) fd, err := open(path, O_RDONLY, 0)
if err != 0 { if err != nil {
return -1 return -1
} }
defer close(fd) defer close(fd)
length: i64 length: i64
if length, err = file_size(fd); err != 0 { if length, err = file_size(fd); err != nil {
return -1 return -1
} }
return length return length
} }
@(require_results)
read_entire_file_from_filename :: proc(name: string, allocator := context.allocator, loc := #caller_location) -> (data: []byte, success: bool) { read_entire_file_from_filename :: proc(name: string, allocator := context.allocator, loc := #caller_location) -> (data: []byte, success: bool) {
context.allocator = allocator err: Error
data, err = read_entire_file_from_filename_or_err(name, allocator, loc)
fd, err := open(name, O_RDONLY, 0) success = err == nil
if err != 0 { return
return nil, false
}
defer close(fd)
return read_entire_file_from_handle(fd, allocator, loc)
} }
@(require_results)
read_entire_file_from_handle :: proc(fd: Handle, allocator := context.allocator, loc := #caller_location) -> (data: []byte, success: bool) { read_entire_file_from_handle :: proc(fd: Handle, allocator := context.allocator, loc := #caller_location) -> (data: []byte, success: bool) {
context.allocator = allocator err: Error
data, err = read_entire_file_from_handle_or_err(fd, allocator, loc)
length: i64 success = err == nil
err: Errno return
if length, err = file_size(fd); err != 0 {
return nil, false
}
if length <= 0 {
return nil, true
}
data = make([]byte, int(length), allocator, loc)
if data == nil {
return nil, false
}
bytes_read, read_err := read_full(fd, data)
if read_err != ERROR_NONE {
delete(data)
return nil, false
}
return data[:bytes_read], true
} }
read_entire_file :: proc { read_entire_file :: proc {
@@ -136,7 +127,50 @@ read_entire_file :: proc {
read_entire_file_from_handle, read_entire_file_from_handle,
} }
@(require_results)
read_entire_file_from_filename_or_err :: proc(name: string, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Error) {
context.allocator = allocator
fd := open(name, O_RDONLY, 0) or_return
defer close(fd)
return read_entire_file_from_handle_or_err(fd, allocator, loc)
}
@(require_results)
read_entire_file_from_handle_or_err :: proc(fd: Handle, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Error) {
context.allocator = allocator
length := file_size(fd) or_return
if length <= 0 {
return nil, nil
}
data = make([]byte, int(length), allocator, loc) or_return
if data == nil {
return nil, nil
}
defer if err != nil {
delete(data, allocator)
}
bytes_read := read_full(fd, data) or_return
data = data[:bytes_read]
return
}
read_entire_file_or_err :: proc {
read_entire_file_from_filename_or_err,
read_entire_file_from_handle_or_err,
}
write_entire_file :: proc(name: string, data: []byte, truncate := true) -> (success: bool) { write_entire_file :: proc(name: string, data: []byte, truncate := true) -> (success: bool) {
return write_entire_file_or_err(name, data, truncate) == nil
}
@(require_results)
write_entire_file_or_err :: proc(name: string, data: []byte, truncate := true) -> Error {
flags: int = O_WRONLY|O_CREATE flags: int = O_WRONLY|O_CREATE
if truncate { if truncate {
flags |= O_TRUNC flags |= O_TRUNC
@@ -148,21 +182,18 @@ write_entire_file :: proc(name: string, data: []byte, truncate := true) -> (succ
mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
} }
fd, err := open(name, flags, mode) fd := open(name, flags, mode) or_return
if err != 0 {
return false
}
defer close(fd) defer close(fd)
_, write_err := write(fd, data) _ = write(fd, data) or_return
return write_err == 0 return nil
} }
write_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Errno) { write_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Error) {
return write(fd, ([^]byte)(data)[:len]) return write(fd, ([^]byte)(data)[:len])
} }
read_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Errno) { read_ptr :: proc(fd: Handle, data: rawptr, len: int) -> (int, Error) {
return read(fd, ([^]byte)(data)[:len]) return read(fd, ([^]byte)(data)[:len])
} }
@@ -173,6 +204,7 @@ heap_alloc :: runtime.heap_alloc
heap_resize :: runtime.heap_resize heap_resize :: runtime.heap_resize
heap_free :: runtime.heap_free heap_free :: runtime.heap_free
@(require_results)
processor_core_count :: proc() -> int { processor_core_count :: proc() -> int {
return _processor_core_count() return _processor_core_count()
} }
+2 -1
View File
@@ -42,13 +42,14 @@ Error :: union #shared_nil {
ERROR_NONE :: Error{} ERROR_NONE :: Error{}
@(require_results)
is_platform_error :: proc(ferr: Error) -> (err: i32, ok: bool) { is_platform_error :: proc(ferr: Error) -> (err: i32, ok: bool) {
v := ferr.(Platform_Error) or_else {} v := ferr.(Platform_Error) or_else {}
return i32(v), i32(v) != 0 return i32(v), i32(v) != 0
} }
@(require_results)
error_string :: proc(ferr: Error) -> string { error_string :: proc(ferr: Error) -> string {
if ferr == nil { if ferr == nil {
return "" return ""
+5 -5
View File
@@ -4,8 +4,8 @@ package os2
import "core:sys/linux" import "core:sys/linux"
@(rodata) @(rodata)
_errno_strings : [linux.Errno]string = { _errno_strings := [linux.Error]string{
.NONE = "Success", .NONE = "",
.EPERM = "Operation not permitted", .EPERM = "Operation not permitted",
.ENOENT = "No such file or directory", .ENOENT = "No such file or directory",
.ESRCH = "No such process", .ESRCH = "No such process",
@@ -142,7 +142,7 @@ _errno_strings : [linux.Errno]string = {
} }
_get_platform_error :: proc(errno: linux.Errno) -> Error { _get_platform_error :: proc(errno: linux.Error) -> Error {
#partial switch errno { #partial switch errno {
case .NONE: case .NONE:
return nil return nil
@@ -158,8 +158,8 @@ _get_platform_error :: proc(errno: linux.Errno) -> Error {
} }
_error_string :: proc(errno: i32) -> string { _error_string :: proc(errno: i32) -> string {
if errno >= 0 && errno <= i32(max(linux.Errno)) { if errno >= 0 && errno <= i32(max(linux.Error)) {
return _errno_strings[linux.Errno(errno)] return _errno_strings[linux.Error(errno)]
} }
return "Unknown Error" return "Unknown Error"
} }
+20 -5
View File
@@ -73,6 +73,24 @@ write_encoded_rune :: proc(f: ^File, r: rune) -> (n: int, err: Error) {
return return
} }
read_at_least :: proc(f: ^File, buf: []byte, min: int) -> (n: int, err: Error) {
if len(buf) < min {
return 0, .Short_Buffer
}
nn := max(int)
for nn > 0 && n < min && err == nil {
nn, err = read(f, buf[n:])
n += nn
}
if n >= min {
err = nil
}
return
}
read_full :: proc(f: ^File, buf: []byte) -> (n: int, err: Error) {
return read_at_least(f, buf, len(buf))
}
write_ptr :: proc(f: ^File, data: rawptr, len: int) -> (n: int, err: Error) { write_ptr :: proc(f: ^File, data: rawptr, len: int) -> (n: int, err: Error) {
return write(f, ([^]byte)(data)[:len]) return write(f, ([^]byte)(data)[:len])
@@ -155,11 +173,8 @@ write_entire_file :: proc(name: string, data: []byte, perm: int, truncate := tru
if truncate { if truncate {
flags |= O_TRUNC flags |= O_TRUNC
} }
f, err := open(name, flags, perm) f := open(name, flags, perm) or_return
if err != nil { _, err := write(f, data)
return err
}
_, err = write(f, data)
if cerr := close(f); cerr != nil && err == nil { if cerr := close(f); cerr != nil && err == nil {
err = cerr err = cerr
} }
+1 -1
View File
@@ -59,7 +59,7 @@ _mkdir_all :: proc(path: string, perm: int) -> Error {
path_bytes[len(path)] = 0 path_bytes[len(path)] = 0
dfd: linux.Fd dfd: linux.Fd
errno: linux.Errno errno: linux.Error
if path_bytes[0] == '/' { if path_bytes[0] == '/' {
dfd, errno = linux.open("/", _OPENDIR_FLAGS) dfd, errno = linux.open("/", _OPENDIR_FLAGS)
path_bytes = path_bytes[1:] path_bytes = path_bytes[1:]
+5 -8
View File
@@ -6,25 +6,22 @@ import "core:time"
import "core:strings" import "core:strings"
import win32 "core:sys/windows" import win32 "core:sys/windows"
_fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (File_Info, Error) { _fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
if f == nil || (^File_Impl)(f.impl).fd == nil { if f == nil || (^File_Impl)(f.impl).fd == nil {
return {}, nil return
} }
path, err := _cleanpath_from_handle(f, allocator) path := _cleanpath_from_handle(f, allocator) or_return
if err != nil {
return {}, err
}
h := _handle(f) h := _handle(f)
switch win32.GetFileType(h) { switch win32.GetFileType(h) {
case win32.FILE_TYPE_PIPE, win32.FILE_TYPE_CHAR: case win32.FILE_TYPE_PIPE, win32.FILE_TYPE_CHAR:
fi := File_Info { fi = File_Info {
fullpath = path, fullpath = path,
name = basename(path), name = basename(path),
type = file_type(h), type = file_type(h),
} }
return fi, nil return
} }
return _file_info_from_get_file_information_by_handle(path, h, allocator) return _file_info_from_get_file_information_by_handle(path, h, allocator)
+419 -268
View File
File diff suppressed because it is too large Load Diff
+29 -24
View File
@@ -2,54 +2,59 @@ package os
import "core:sys/es" import "core:sys/es"
Handle :: distinct int; Handle :: distinct int
Errno :: distinct int; _Platform_Error :: enum i32 {NONE}
ERROR_NONE :: (Errno) (es.SUCCESS); // ERROR_NONE :: Error(es.SUCCESS)
O_RDONLY :: 0x1; O_RDONLY :: 0x1
O_WRONLY :: 0x2; O_WRONLY :: 0x2
O_CREATE :: 0x4; O_CREATE :: 0x4
O_TRUNC :: 0x8; O_TRUNC :: 0x8
stderr : Handle = 0; stderr : Handle = 0
current_thread_id :: proc "contextless" () -> int { current_thread_id :: proc "contextless" () -> int {
return (int) (es.ThreadGetID(es.CURRENT_THREAD)); return (int) (es.ThreadGetID(es.CURRENT_THREAD))
} }
heap_alloc :: proc(size: int, zero_memory := true) -> rawptr { heap_alloc :: proc(size: int, zero_memory := true) -> rawptr {
return es.HeapAllocate(size, zero_memory); return es.HeapAllocate(size, zero_memory)
} }
heap_free :: proc(ptr: rawptr) { heap_free :: proc(ptr: rawptr) {
es.HeapFree(ptr); es.HeapFree(ptr)
} }
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr { heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
return es.HeapReallocate(ptr, new_size, false); return es.HeapReallocate(ptr, new_size, false)
} }
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) { open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Error) {
return (Handle) (0), (Errno) (1); return (Handle) (0), (Error) (1)
} }
close :: proc(fd: Handle) -> Errno { close :: proc(fd: Handle) -> Error {
return (Errno) (1); return (Error) (1)
} }
file_size :: proc(fd: Handle) -> (i64, Errno) { file_size :: proc(fd: Handle) -> (i64, Error) {
return (i64) (0), (Errno) (1); return (i64) (0), (Error) (1)
} }
read :: proc(fd: Handle, data: []byte) -> (int, Errno) { read :: proc(fd: Handle, data: []byte) -> (int, Error) {
return (int) (0), (Errno) (1); return (int) (0), (Error) (1)
} }
write :: proc(fd: Handle, data: []u8) -> (int, Errno) { write :: proc(fd: Handle, data: []u8) -> (int, Error) {
return (int) (0), (Errno) (1); return (int) (0), (Error) (1)
} }
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
return (i64) (0), (Errno) (1); return (i64) (0), (Error) (1)
} }
flush :: proc(fd: Handle) -> Error {
// do nothing
return nil
}
+334 -196
View File
@@ -9,105 +9,200 @@ import "core:c"
Handle :: distinct i32 Handle :: distinct i32
File_Time :: distinct u64 File_Time :: distinct u64
Errno :: distinct i32
INVALID_HANDLE :: ~Handle(0) INVALID_HANDLE :: ~Handle(0)
ERROR_NONE: Errno : 0 _Platform_Error :: enum i32 {
EPERM: Errno : 1 NONE = 0,
ENOENT: Errno : 2 EPERM = 1,
ESRCH: Errno : 3 ENOENT = 2,
EINTR: Errno : 4 ESRCH = 3,
EIO: Errno : 5 EINTR = 4,
ENXIO: Errno : 6 EIO = 5,
E2BIG: Errno : 7 ENXIO = 6,
ENOEXEC: Errno : 8 E2BIG = 7,
EBADF: Errno : 9 ENOEXEC = 8,
ECHILD: Errno : 10 EBADF = 9,
EBEADLK: Errno : 11 ECHILD = 10,
ENOMEM: Errno : 12 EBEADLK = 11,
EACCESS: Errno : 13 ENOMEM = 12,
EFAULT: Errno : 14 EACCESS = 13,
ENOTBLK: Errno : 15 EFAULT = 14,
EBUSY: Errno : 16 ENOTBLK = 15,
EEXIST: Errno : 17 EBUSY = 16,
EXDEV: Errno : 18 EEXIST = 17,
ENODEV: Errno : 19 EXDEV = 18,
ENOTDIR: Errno : 20 ENODEV = 19,
EISDIR: Errno : 21 ENOTDIR = 20,
EINVAL: Errno : 22 EISDIR = 21,
ENFILE: Errno : 23 EINVAL = 22,
EMFILE: Errno : 24 ENFILE = 23,
ENOTTY: Errno : 25 EMFILE = 24,
ETXTBSY: Errno : 26 ENOTTY = 25,
EFBIG: Errno : 27 ETXTBSY = 26,
ENOSPC: Errno : 28 EFBIG = 27,
ESPIPE: Errno : 29 ENOSPC = 28,
EROFS: Errno : 30 ESPIPE = 29,
EMLINK: Errno : 31 EROFS = 30,
EPIPE: Errno : 32 EMLINK = 31,
EDOM: Errno : 33 EPIPE = 32,
ERANGE: Errno : 34 /* Result too large */ EDOM = 33,
EAGAIN: Errno : 35 ERANGE = 34, /* Result too large */
EINPROGRESS: Errno : 36 EAGAIN = 35,
EALREADY: Errno : 37 EINPROGRESS = 36,
ENOTSOCK: Errno : 38 EALREADY = 37,
EDESTADDRREQ: Errno : 39 ENOTSOCK = 38,
EMSGSIZE: Errno : 40 EDESTADDRREQ = 39,
EPROTOTYPE: Errno : 41 EMSGSIZE = 40,
ENOPROTOOPT: Errno : 42 EPROTOTYPE = 41,
EPROTONOSUPPORT: Errno : 43 ENOPROTOOPT = 42,
ESOCKTNOSUPPORT: Errno : 44 EPROTONOSUPPORT = 43,
EOPNOTSUPP: Errno : 45 ESOCKTNOSUPPORT = 44,
EPFNOSUPPORT: Errno : 46 EOPNOTSUPP = 45,
EAFNOSUPPORT: Errno : 47 EPFNOSUPPORT = 46,
EADDRINUSE: Errno : 48 EAFNOSUPPORT = 47,
EADDRNOTAVAIL: Errno : 49 EADDRINUSE = 48,
ENETDOWN: Errno : 50 EADDRNOTAVAIL = 49,
ENETUNREACH: Errno : 51 ENETDOWN = 50,
ENETRESET: Errno : 52 ENETUNREACH = 51,
ECONNABORTED: Errno : 53 ENETRESET = 52,
ECONNRESET: Errno : 54 ECONNABORTED = 53,
ENOBUFS: Errno : 55 ECONNRESET = 54,
EISCONN: Errno : 56 ENOBUFS = 55,
ENOTCONN: Errno : 57 EISCONN = 56,
ESHUTDOWN: Errno : 58 ENOTCONN = 57,
ETIMEDOUT: Errno : 60 ESHUTDOWN = 58,
ECONNREFUSED: Errno : 61 ETIMEDOUT = 60,
ELOOP: Errno : 62 ECONNREFUSED = 61,
ENAMETOOLING: Errno : 63 ELOOP = 62,
EHOSTDOWN: Errno : 64 ENAMETOOLING = 63,
EHOSTUNREACH: Errno : 65 EHOSTDOWN = 64,
ENOTEMPTY: Errno : 66 EHOSTUNREACH = 65,
EPROCLIM: Errno : 67 ENOTEMPTY = 66,
EUSERS: Errno : 68 EPROCLIM = 67,
EDQUOT: Errno : 69 EUSERS = 68,
ESTALE: Errno : 70 EDQUOT = 69,
EBADRPC: Errno : 72 ESTALE = 70,
ERPCMISMATCH: Errno : 73 EBADRPC = 72,
EPROGUNAVAIL: Errno : 74 ERPCMISMATCH = 73,
EPROGMISMATCH: Errno : 75 EPROGUNAVAIL = 74,
EPROCUNAVAIL: Errno : 76 EPROGMISMATCH = 75,
ENOLCK: Errno : 77 EPROCUNAVAIL = 76,
ENOSYS: Errno : 78 ENOLCK = 77,
EFTYPE: Errno : 79 ENOSYS = 78,
EAUTH: Errno : 80 EFTYPE = 79,
ENEEDAUTH: Errno : 81 EAUTH = 80,
EIDRM: Errno : 82 ENEEDAUTH = 81,
ENOMSG: Errno : 83 EIDRM = 82,
EOVERFLOW: Errno : 84 ENOMSG = 83,
ECANCELED: Errno : 85 EOVERFLOW = 84,
EILSEQ: Errno : 86 ECANCELED = 85,
ENOATTR: Errno : 87 EILSEQ = 86,
EDOOFUS: Errno : 88 ENOATTR = 87,
EBADMSG: Errno : 89 EDOOFUS = 88,
EMULTIHOP: Errno : 90 EBADMSG = 89,
ENOLINK: Errno : 91 EMULTIHOP = 90,
EPROTO: Errno : 92 ENOLINK = 91,
ENOTCAPABLE: Errno : 93 EPROTO = 92,
ECAPMODE: Errno : 94 ENOTCAPABLE = 93,
ENOTRECOVERABLE: Errno : 95 ECAPMODE = 94,
EOWNERDEAD: Errno : 96 ENOTRECOVERABLE = 95,
EOWNERDEAD = 96,
}
EPERM :: Platform_Error.EPERM
ENOENT :: Platform_Error.ENOENT
ESRCH :: Platform_Error.ESRCH
EINTR :: Platform_Error.EINTR
EIO :: Platform_Error.EIO
ENXIO :: Platform_Error.ENXIO
E2BIG :: Platform_Error.E2BIG
ENOEXEC :: Platform_Error.ENOEXEC
EBADF :: Platform_Error.EBADF
ECHILD :: Platform_Error.ECHILD
EBEADLK :: Platform_Error.EBEADLK
ENOMEM :: Platform_Error.ENOMEM
EACCESS :: Platform_Error.EACCESS
EFAULT :: Platform_Error.EFAULT
ENOTBLK :: Platform_Error.ENOTBLK
EBUSY :: Platform_Error.EBUSY
EEXIST :: Platform_Error.EEXIST
EXDEV :: Platform_Error.EXDEV
ENODEV :: Platform_Error.ENODEV
ENOTDIR :: Platform_Error.ENOTDIR
EISDIR :: Platform_Error.EISDIR
EINVAL :: Platform_Error.EINVAL
ENFILE :: Platform_Error.ENFILE
EMFILE :: Platform_Error.EMFILE
ENOTTY :: Platform_Error.ENOTTY
ETXTBSY :: Platform_Error.ETXTBSY
EFBIG :: Platform_Error.EFBIG
ENOSPC :: Platform_Error.ENOSPC
ESPIPE :: Platform_Error.ESPIPE
EROFS :: Platform_Error.EROFS
EMLINK :: Platform_Error.EMLINK
EPIPE :: Platform_Error.EPIPE
EDOM :: Platform_Error.EDOM
ERANGE :: Platform_Error.ERANGE
EAGAIN :: Platform_Error.EAGAIN
EINPROGRESS :: Platform_Error.EINPROGRESS
EALREADY :: Platform_Error.EALREADY
ENOTSOCK :: Platform_Error.ENOTSOCK
EDESTADDRREQ :: Platform_Error.EDESTADDRREQ
EMSGSIZE :: Platform_Error.EMSGSIZE
EPROTOTYPE :: Platform_Error.EPROTOTYPE
ENOPROTOOPT :: Platform_Error.ENOPROTOOPT
EPROTONOSUPPORT :: Platform_Error.EPROTONOSUPPORT
ESOCKTNOSUPPORT :: Platform_Error.ESOCKTNOSUPPORT
EOPNOTSUPP :: Platform_Error.EOPNOTSUPP
EPFNOSUPPORT :: Platform_Error.EPFNOSUPPORT
EAFNOSUPPORT :: Platform_Error.EAFNOSUPPORT
EADDRINUSE :: Platform_Error.EADDRINUSE
EADDRNOTAVAIL :: Platform_Error.EADDRNOTAVAIL
ENETDOWN :: Platform_Error.ENETDOWN
ENETUNREACH :: Platform_Error.ENETUNREACH
ENETRESET :: Platform_Error.ENETRESET
ECONNABORTED :: Platform_Error.ECONNABORTED
ECONNRESET :: Platform_Error.ECONNRESET
ENOBUFS :: Platform_Error.ENOBUFS
EISCONN :: Platform_Error.EISCONN
ENOTCONN :: Platform_Error.ENOTCONN
ESHUTDOWN :: Platform_Error.ESHUTDOWN
ETIMEDOUT :: Platform_Error.ETIMEDOUT
ECONNREFUSED :: Platform_Error.ECONNREFUSED
ELOOP :: Platform_Error.ELOOP
ENAMETOOLING :: Platform_Error.ENAMETOOLING
EHOSTDOWN :: Platform_Error.EHOSTDOWN
EHOSTUNREACH :: Platform_Error.EHOSTUNREACH
ENOTEMPTY :: Platform_Error.ENOTEMPTY
EPROCLIM :: Platform_Error.EPROCLIM
EUSERS :: Platform_Error.EUSERS
EDQUOT :: Platform_Error.EDQUOT
ESTALE :: Platform_Error.ESTALE
EBADRPC :: Platform_Error.EBADRPC
ERPCMISMATCH :: Platform_Error.ERPCMISMATCH
EPROGUNAVAIL :: Platform_Error.EPROGUNAVAIL
EPROGMISMATCH :: Platform_Error.EPROGMISMATCH
EPROCUNAVAIL :: Platform_Error.EPROCUNAVAIL
ENOLCK :: Platform_Error.ENOLCK
ENOSYS :: Platform_Error.ENOSYS
EFTYPE :: Platform_Error.EFTYPE
EAUTH :: Platform_Error.EAUTH
ENEEDAUTH :: Platform_Error.ENEEDAUTH
EIDRM :: Platform_Error.EIDRM
ENOMSG :: Platform_Error.ENOMSG
EOVERFLOW :: Platform_Error.EOVERFLOW
ECANCELED :: Platform_Error.ECANCELED
EILSEQ :: Platform_Error.EILSEQ
ENOATTR :: Platform_Error.ENOATTR
EDOOFUS :: Platform_Error.EDOOFUS
EBADMSG :: Platform_Error.EBADMSG
EMULTIHOP :: Platform_Error.EMULTIHOP
ENOLINK :: Platform_Error.ENOLINK
EPROTO :: Platform_Error.EPROTO
ENOTCAPABLE :: Platform_Error.ENOTCAPABLE
ECAPMODE :: Platform_Error.ECAPMODE
ENOTRECOVERABLE :: Platform_Error.ENOTRECOVERABLE
EOWNERDEAD :: Platform_Error.EOWNERDEAD
O_RDONLY :: 0x00000 O_RDONLY :: 0x00000
O_WRONLY :: 0x00001 O_WRONLY :: 0x00001
@@ -258,13 +353,13 @@ S_ISGID :: 0o2000 // Set group id on execution
S_ISVTX :: 0o1000 // Directory restrcted delete S_ISVTX :: 0o1000 // Directory restrcted delete
S_ISLNK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFLNK } @(require_results) S_ISLNK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFLNK }
S_ISREG :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFREG } @(require_results) S_ISREG :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFREG }
S_ISDIR :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFDIR } @(require_results) S_ISDIR :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFDIR }
S_ISCHR :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFCHR } @(require_results) S_ISCHR :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFCHR }
S_ISBLK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFBLK } @(require_results) S_ISBLK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFBLK }
S_ISFIFO :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFIFO } @(require_results) S_ISFIFO :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFIFO }
S_ISSOCK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFSOCK } @(require_results) S_ISSOCK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFSOCK }
F_OK :: 0 // Test for file existance F_OK :: 0 // Test for file existance
X_OK :: 1 // Test for execute permission X_OK :: 1 // Test for execute permission
@@ -274,7 +369,7 @@ R_OK :: 4 // Test for read permission
F_KINFO :: 22 F_KINFO :: 22
foreign libc { foreign libc {
@(link_name="__error") __errno_location :: proc() -> ^c.int --- @(link_name="__error") __Error_location :: proc() -> ^c.int ---
@(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle --- @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle ---
@(link_name="close") _unix_close :: proc(fd: Handle) -> c.int --- @(link_name="close") _unix_close :: proc(fd: Handle) -> c.int ---
@@ -320,30 +415,38 @@ foreign dl {
@(link_name="pthread_getthreadid_np") pthread_getthreadid_np :: proc() -> c.int --- @(link_name="pthread_getthreadid_np") pthread_getthreadid_np :: proc() -> c.int ---
} }
@(require_results)
is_path_separator :: proc(r: rune) -> bool { is_path_separator :: proc(r: rune) -> bool {
return r == '/' return r == '/'
} }
get_last_error :: proc "contextless" () -> int { @(require_results, no_instrumentation)
return int(__errno_location()^) get_last_error :: proc "contextless" () -> Error {
return Platform_Error(__Error_location()^)
} }
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) { @(require_results)
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
handle := _unix_open(cstr, c.int(flags), c.int(mode)) handle := _unix_open(cstr, c.int(flags), c.int(mode))
if handle == -1 { if handle == -1 {
return INVALID_HANDLE, Errno(get_last_error()) return INVALID_HANDLE, get_last_error()
} }
return handle, ERROR_NONE return handle, nil
} }
close :: proc(fd: Handle) -> Errno { close :: proc(fd: Handle) -> Error {
result := _unix_close(fd) result := _unix_close(fd)
if result == -1 { if result == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
}
flush :: proc(fd: Handle) -> Error {
// do nothing
return nil
} }
// If you read or write more than `INT_MAX` bytes, FreeBSD returns `EINVAL`. // If you read or write more than `INT_MAX` bytes, FreeBSD returns `EINVAL`.
@@ -354,124 +457,148 @@ close :: proc(fd: Handle) -> Errno {
@(private) @(private)
MAX_RW :: 1 << 30 MAX_RW :: 1 << 30
read :: proc(fd: Handle, data: []byte) -> (int, Errno) { read :: proc(fd: Handle, data: []byte) -> (int, Error) {
to_read := min(c.size_t(len(data)), MAX_RW) to_read := min(c.size_t(len(data)), MAX_RW)
bytes_read := _unix_read(fd, &data[0], to_read) bytes_read := _unix_read(fd, &data[0], to_read)
if bytes_read == -1 { if bytes_read == -1 {
return -1, Errno(get_last_error()) return -1, get_last_error()
} }
return int(bytes_read), ERROR_NONE return int(bytes_read), nil
} }
write :: proc(fd: Handle, data: []byte) -> (int, Errno) { write :: proc(fd: Handle, data: []byte) -> (int, Error) {
if len(data) == 0 { if len(data) == 0 {
return 0, ERROR_NONE return 0, nil
} }
to_write := min(c.size_t(len(data)), MAX_RW) to_write := min(c.size_t(len(data)), MAX_RW)
bytes_written := _unix_write(fd, &data[0], to_write) bytes_written := _unix_write(fd, &data[0], to_write)
if bytes_written == -1 { if bytes_written == -1 {
return -1, Errno(get_last_error()) return -1, get_last_error()
} }
return int(bytes_written), ERROR_NONE return int(bytes_written), nil
} }
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
curr := seek(fd, offset, SEEK_CUR) or_return
n, err = read(fd, data)
_, err1 := seek(fd, curr, SEEK_SET)
if err1 != nil && err == nil {
err = err1
}
return
}
write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
curr := seek(fd, offset, SEEK_CUR) or_return
n, err = write(fd, data)
_, err1 := seek(fd, curr, SEEK_SET)
if err1 != nil && err == nil {
err = err1
}
return
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
res := _unix_seek(fd, offset, c.int(whence)) res := _unix_seek(fd, offset, c.int(whence))
if res == -1 { if res == -1 {
return -1, Errno(get_last_error()) return -1, get_last_error()
} }
return res, ERROR_NONE return res, nil
} }
file_size :: proc(fd: Handle) -> (i64, Errno) { @(require_results)
s, err := _fstat(fd) file_size :: proc(fd: Handle) -> (size: i64, err: Error) {
if err != ERROR_NONE { size = -1
return -1, err s := _fstat(fd) or_return
} size = s.size
return s.size, ERROR_NONE return
} }
rename :: proc(old_path, new_path: string) -> Errno { rename :: proc(old_path, new_path: string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator) old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator)
new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator) new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator)
res := _unix_rename(old_path_cstr, new_path_cstr) res := _unix_rename(old_path_cstr, new_path_cstr)
if res == -1 { if res == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
remove :: proc(path: string) -> Errno { remove :: proc(path: string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator) path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_unlink(path_cstr) res := _unix_unlink(path_cstr)
if res == -1 { if res == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
make_directory :: proc(path: string, mode: mode_t = 0o775) -> Errno { make_directory :: proc(path: string, mode: mode_t = 0o775) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator) path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_mkdir(path_cstr, mode) res := _unix_mkdir(path_cstr, mode)
if res == -1 { if res == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
remove_directory :: proc(path: string) -> Errno { remove_directory :: proc(path: string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator) path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_rmdir(path_cstr) res := _unix_rmdir(path_cstr)
if res == -1 { if res == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
@(require_results)
is_file_handle :: proc(fd: Handle) -> bool { is_file_handle :: proc(fd: Handle) -> bool {
s, err := _fstat(fd) s, err := _fstat(fd)
if err != ERROR_NONE { if err != nil {
return false return false
} }
return S_ISREG(s.mode) return S_ISREG(s.mode)
} }
@(require_results)
is_file_path :: proc(path: string, follow_links: bool = true) -> bool { is_file_path :: proc(path: string, follow_links: bool = true) -> bool {
s: OS_Stat s: OS_Stat
err: Errno err: Error
if follow_links { if follow_links {
s, err = _stat(path) s, err = _stat(path)
} else { } else {
s, err = _lstat(path) s, err = _lstat(path)
} }
if err != ERROR_NONE { if err != nil {
return false return false
} }
return S_ISREG(s.mode) return S_ISREG(s.mode)
} }
@(require_results)
is_dir_handle :: proc(fd: Handle) -> bool { is_dir_handle :: proc(fd: Handle) -> bool {
s, err := _fstat(fd) s, err := _fstat(fd)
if err != ERROR_NONE { if err != nil {
return false return false
} }
return S_ISDIR(s.mode) return S_ISDIR(s.mode)
} }
@(require_results)
is_dir_path :: proc(path: string, follow_links: bool = true) -> bool { is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
s: OS_Stat s: OS_Stat
err: Errno err: Error
if follow_links { if follow_links {
s, err = _stat(path) s, err = _stat(path)
} else { } else {
s, err = _lstat(path) s, err = _lstat(path)
} }
if err != ERROR_NONE { if err != nil {
return false return false
} }
return S_ISDIR(s.mode) return S_ISDIR(s.mode)
@@ -490,38 +617,40 @@ stderr: Handle = 2
last_write_time :: proc(fd: Handle) -> File_Time {} last_write_time :: proc(fd: Handle) -> File_Time {}
last_write_time_by_name :: proc(name: string) -> File_Time {} last_write_time_by_name :: proc(name: string) -> File_Time {}
*/ */
last_write_time :: proc(fd: Handle) -> (File_Time, Errno) { @(require_results)
last_write_time :: proc(fd: Handle) -> (File_Time, Error) {
s, err := _fstat(fd) s, err := _fstat(fd)
if err != ERROR_NONE { if err != nil {
return 0, err return 0, err
} }
modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
return File_Time(modified), ERROR_NONE return File_Time(modified), nil
} }
last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) { @(require_results)
last_write_time_by_name :: proc(name: string) -> (File_Time, Error) {
s, err := _stat(name) s, err := _stat(name)
if err != ERROR_NONE { if err != nil {
return 0, err return 0, err
} }
modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
return File_Time(modified), ERROR_NONE return File_Time(modified), nil
} }
@private @(private, require_results)
_stat :: proc(path: string) -> (OS_Stat, Errno) { _stat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
s: OS_Stat = --- s: OS_Stat = ---
result := _unix_lstat(cstr, &s) result := _unix_lstat(cstr, &s)
if result == -1 { if result == -1 {
return s, Errno(get_last_error()) return s, get_last_error()
} }
return s, ERROR_NONE return s, nil
} }
@private @(private, require_results)
_lstat :: proc(path: string) -> (OS_Stat, Errno) { _lstat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -529,54 +658,53 @@ _lstat :: proc(path: string) -> (OS_Stat, Errno) {
s: OS_Stat = --- s: OS_Stat = ---
res := _unix_lstat(cstr, &s) res := _unix_lstat(cstr, &s)
if res == -1 { if res == -1 {
return s, Errno(get_last_error()) return s, get_last_error()
} }
return s, ERROR_NONE return s, nil
} }
@private @(private, require_results)
_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) { _fstat :: proc(fd: Handle) -> (OS_Stat, Error) {
s: OS_Stat = --- s: OS_Stat = ---
result := _unix_fstat(fd, &s) result := _unix_fstat(fd, &s)
if result == -1 { if result == -1 {
return s, Errno(get_last_error()) return s, get_last_error()
} }
return s, ERROR_NONE return s, nil
} }
@private @(private, require_results)
_fdopendir :: proc(fd: Handle) -> (Dir, Errno) { _fdopendir :: proc(fd: Handle) -> (Dir, Error) {
dirp := _unix_fdopendir(fd) dirp := _unix_fdopendir(fd)
if dirp == cast(Dir)nil { if dirp == cast(Dir)nil {
return nil, Errno(get_last_error()) return nil, get_last_error()
} }
return dirp, ERROR_NONE return dirp, nil
} }
@private @(private)
_closedir :: proc(dirp: Dir) -> Errno { _closedir :: proc(dirp: Dir) -> Error {
rc := _unix_closedir(dirp) rc := _unix_closedir(dirp)
if rc != 0 { if rc != 0 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
@private @(private)
_rewinddir :: proc(dirp: Dir) { _rewinddir :: proc(dirp: Dir) {
_unix_rewinddir(dirp) _unix_rewinddir(dirp)
} }
@private @(private, require_results)
_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) { _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Error, end_of_stream: bool) {
result: ^Dirent result: ^Dirent
rc := _unix_readdir_r(dirp, &entry, &result) rc := _unix_readdir_r(dirp, &entry, &result)
if rc != 0 { if rc != 0 {
err = Errno(get_last_error()) err = get_last_error()
return return
} }
err = ERROR_NONE
if result == nil { if result == nil {
end_of_stream = true end_of_stream = true
@@ -586,8 +714,8 @@ _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool)
return return
} }
@private @(private, require_results)
_readlink :: proc(path: string) -> (string, Errno) { _readlink :: proc(path: string) -> (string, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator) runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
path_cstr := strings.clone_to_cstring(path, context.temp_allocator) path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -598,20 +726,21 @@ _readlink :: proc(path: string) -> (string, Errno) {
rc := _unix_readlink(path_cstr, &(buf[0]), bufsz) rc := _unix_readlink(path_cstr, &(buf[0]), bufsz)
if rc == -1 { if rc == -1 {
delete(buf) delete(buf)
return "", Errno(get_last_error()) return "", get_last_error()
} else if rc == int(bufsz) { } else if rc == int(bufsz) {
bufsz += MAX_PATH bufsz += MAX_PATH
delete(buf) delete(buf)
buf = make([]byte, bufsz) buf = make([]byte, bufsz)
} else { } else {
return strings.string_from_ptr(&buf[0], rc), ERROR_NONE return strings.string_from_ptr(&buf[0], rc), nil
} }
} }
return "", Errno{} return "", Error{}
} }
absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) { @(require_results)
absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {
// NOTE(Feoramund): The situation isn't ideal, but this was the best way I // NOTE(Feoramund): The situation isn't ideal, but this was the best way I
// could find to implement this. There are a couple outstanding bug reports // could find to implement this. There are a couple outstanding bug reports
// regarding the desire to retrieve an absolute path from a handle, but to // regarding the desire to retrieve an absolute path from a handle, but to
@@ -626,14 +755,15 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) {
res := _unix_fcntl(fd, F_KINFO, cast(uintptr)&kinfo) res := _unix_fcntl(fd, F_KINFO, cast(uintptr)&kinfo)
if res == -1 { if res == -1 {
return "", Errno(get_last_error()) return "", get_last_error()
} }
path := strings.clone_from_cstring_bounded(cast(cstring)&kinfo.path[0], len(kinfo.path)) path := strings.clone_from_cstring_bounded(cast(cstring)&kinfo.path[0], len(kinfo.path))
return path, ERROR_NONE return path, nil
} }
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) { @(require_results)
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
rel := rel rel := rel
if rel == "" { if rel == "" {
rel = "." rel = "."
@@ -644,27 +774,28 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
path_ptr := _unix_realpath(rel_cstr, nil) path_ptr := _unix_realpath(rel_cstr, nil)
if path_ptr == nil { if path_ptr == nil {
return "", Errno(get_last_error()) return "", get_last_error()
} }
defer _unix_free(path_ptr) defer _unix_free(path_ptr)
path = strings.clone(string(cstring(path_ptr))) path = strings.clone(string(cstring(path_ptr)))
return path, ERROR_NONE return path, nil
} }
access :: proc(path: string, mask: int) -> (bool, Errno) { access :: proc(path: string, mask: int) -> (bool, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
result := _unix_access(cstr, c.int(mask)) result := _unix_access(cstr, c.int(mask))
if result == -1 { if result == -1 {
return false, Errno(get_last_error()) return false, get_last_error()
} }
return true, ERROR_NONE return true, nil
} }
@(require_results)
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
@@ -676,11 +807,13 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
return strings.clone(string(cstr), allocator), true return strings.clone(string(cstr), allocator), true
} }
@(require_results)
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) { get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
value, _ = lookup_env(key, allocator) value, _ = lookup_env(key, allocator)
return return
} }
@(require_results)
get_current_directory :: proc() -> string { get_current_directory :: proc() -> string {
// NOTE(tetra): I would use PATH_MAX here, but I was not able to find // NOTE(tetra): I would use PATH_MAX here, but I was not able to find
// an authoritative value for it across all systems. // an authoritative value for it across all systems.
@@ -692,7 +825,7 @@ get_current_directory :: proc() -> string {
if cwd != nil { if cwd != nil {
return string(cwd) return string(cwd)
} }
if Errno(get_last_error()) != ERANGE { if get_last_error() != ERANGE {
delete(buf) delete(buf)
return "" return ""
} }
@@ -701,14 +834,14 @@ get_current_directory :: proc() -> string {
unreachable() unreachable()
} }
set_current_directory :: proc(path: string) -> (err: Errno) { set_current_directory :: proc(path: string) -> (err: Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_chdir(cstr) res := _unix_chdir(cstr)
if res == -1 { if res == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
exit :: proc "contextless" (code: int) -> ! { exit :: proc "contextless" (code: int) -> ! {
@@ -716,16 +849,19 @@ exit :: proc "contextless" (code: int) -> ! {
_unix_exit(c.int(code)) _unix_exit(c.int(code))
} }
@(require_results)
current_thread_id :: proc "contextless" () -> int { current_thread_id :: proc "contextless" () -> int {
return cast(int) pthread_getthreadid_np() return cast(int) pthread_getthreadid_np()
} }
@(require_results)
dlopen :: proc(filename: string, flags: int) -> rawptr { dlopen :: proc(filename: string, flags: int) -> rawptr {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(filename, context.temp_allocator) cstr := strings.clone_to_cstring(filename, context.temp_allocator)
handle := _unix_dlopen(cstr, c.int(flags)) handle := _unix_dlopen(cstr, c.int(flags))
return handle return handle
} }
@(require_results)
dlsym :: proc(handle: rawptr, symbol: string) -> rawptr { dlsym :: proc(handle: rawptr, symbol: string) -> rawptr {
assert(handle != nil) assert(handle != nil)
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
@@ -741,6 +877,7 @@ dlerror :: proc() -> string {
return string(_unix_dlerror()) return string(_unix_dlerror())
} }
@(require_results)
get_page_size :: proc() -> int { get_page_size :: proc() -> int {
// NOTE(tetra): The page size never changes, so why do anything complicated // NOTE(tetra): The page size never changes, so why do anything complicated
// if we don't have to. // if we don't have to.
@@ -753,7 +890,7 @@ get_page_size :: proc() -> int {
return page_size return page_size
} }
@(private) @(private, require_results)
_processor_core_count :: proc() -> int { _processor_core_count :: proc() -> int {
count : int = 0 count : int = 0
count_size := size_of(count) count_size := size_of(count)
@@ -767,6 +904,7 @@ _processor_core_count :: proc() -> int {
} }
@(require_results)
_alloc_command_line_arguments :: proc() -> []string { _alloc_command_line_arguments :: proc() -> []string {
res := make([]string, len(runtime.args__)) res := make([]string, len(runtime.args__))
for arg, i in runtime.args__ { for arg, i in runtime.args__ {
+101 -69
View File
@@ -10,16 +10,14 @@ import "core:sys/haiku"
Handle :: i32 Handle :: i32
Pid :: i32 Pid :: i32
File_Time :: i64 File_Time :: i64
Errno :: i32 _Platform_Error :: haiku.Errno
MAX_PATH :: haiku.PATH_MAX MAX_PATH :: haiku.PATH_MAX
ENOSYS :: int(haiku.Errno.POSIX_ERROR_BASE) + 9 ENOSYS :: _Platform_Error(i32(haiku.Errno.POSIX_ERROR_BASE) + 9)
INVALID_HANDLE :: ~Handle(0) INVALID_HANDLE :: ~Handle(0)
ERROR_NONE: Errno: 0
stdin: Handle = 0 stdin: Handle = 0
stdout: Handle = 1 stdout: Handle = 1
stderr: Handle = 2 stderr: Handle = 2
@@ -121,7 +119,7 @@ S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK
foreign libc { foreign libc {
@(link_name="_errnop") __error :: proc() -> ^c.int --- @(link_name="_errorp") __error :: proc() -> ^c.int ---
@(link_name="fork") _unix_fork :: proc() -> pid_t --- @(link_name="fork") _unix_fork :: proc() -> pid_t ---
@(link_name="getthrid") _unix_getthrid :: proc() -> int --- @(link_name="getthrid") _unix_getthrid :: proc() -> int ---
@@ -179,38 +177,47 @@ Dirent :: struct {
Dir :: distinct rawptr // DIR* Dir :: distinct rawptr // DIR*
@(require_results)
is_path_separator :: proc(r: rune) -> bool { is_path_separator :: proc(r: rune) -> bool {
return r == '/' return r == '/'
} }
get_last_error :: proc "contextless" () -> int { @(require_results, no_instrumentation)
return int(__error()^) get_last_error :: proc "contextless" () -> Error {
return Platform_Error(__error()^)
} }
fork :: proc() -> (Pid, Errno) { @(require_results)
fork :: proc() -> (Pid, Error) {
pid := _unix_fork() pid := _unix_fork()
if pid == -1 { if pid == -1 {
return Pid(-1), Errno(get_last_error()) return Pid(-1), get_last_error()
} }
return Pid(pid), ERROR_NONE return Pid(pid), nil
} }
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) { @(require_results)
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
handle := _unix_open(cstr, c.int(flags), c.int(mode)) handle := _unix_open(cstr, c.int(flags), c.int(mode))
if handle == -1 { if handle == -1 {
return INVALID_HANDLE, Errno(get_last_error()) return INVALID_HANDLE, get_last_error()
} }
return handle, ERROR_NONE return handle, nil
} }
close :: proc(fd: Handle) -> Errno { close :: proc(fd: Handle) -> Error {
result := _unix_close(fd) result := _unix_close(fd)
if result == -1 { if result == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
}
flush :: proc(fd: Handle) -> Error {
// do nothing
return nil
} }
// In practice a read/write call would probably never read/write these big buffers all at once, // In practice a read/write call would probably never read/write these big buffers all at once,
@@ -220,47 +227,69 @@ close :: proc(fd: Handle) -> Errno {
@(private) @(private)
MAX_RW :: 1 << 30 MAX_RW :: 1 << 30
read :: proc(fd: Handle, data: []byte) -> (int, Errno) { read :: proc(fd: Handle, data: []byte) -> (int, Error) {
to_read := min(c.size_t(len(data)), MAX_RW) to_read := min(c.size_t(len(data)), MAX_RW)
bytes_read := _unix_read(fd, &data[0], to_read) bytes_read := _unix_read(fd, &data[0], to_read)
if bytes_read == -1 { if bytes_read == -1 {
return -1, Errno(get_last_error()) return -1, get_last_error()
} }
return int(bytes_read), ERROR_NONE return int(bytes_read), nil
} }
write :: proc(fd: Handle, data: []byte) -> (int, Errno) { write :: proc(fd: Handle, data: []byte) -> (int, Error) {
if len(data) == 0 { if len(data) == 0 {
return 0, ERROR_NONE return 0, nil
} }
to_write := min(c.size_t(len(data)), MAX_RW) to_write := min(c.size_t(len(data)), MAX_RW)
bytes_written := _unix_write(fd, &data[0], to_write) bytes_written := _unix_write(fd, &data[0], to_write)
if bytes_written == -1 { if bytes_written == -1 {
return -1, Errno(get_last_error()) return -1, get_last_error()
} }
return int(bytes_written), ERROR_NONE return int(bytes_written), nil
} }
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
curr := seek(fd, offset, SEEK_CUR) or_return
n, err = read(fd, data)
_, err1 := seek(fd, curr, SEEK_SET)
if err1 != nil && err == nil {
err = err1
}
return
}
write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
curr := seek(fd, offset, SEEK_CUR) or_return
n, err = write(fd, data)
_, err1 := seek(fd, curr, SEEK_SET)
if err1 != nil && err == nil {
err = err1
}
return
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
res := _unix_seek(fd, offset, c.int(whence)) res := _unix_seek(fd, offset, c.int(whence))
if res == -1 { if res == -1 {
return -1, Errno(get_last_error()) return -1, get_last_error()
} }
return res, ERROR_NONE return res, nil
} }
file_size :: proc(fd: Handle) -> (i64, Errno) { @(require_results)
file_size :: proc(fd: Handle) -> (i64, Error) {
s, err := _fstat(fd) s, err := _fstat(fd)
if err != ERROR_NONE { if err != nil {
return -1, err return -1, err
} }
return s.size, ERROR_NONE return s.size, nil
} }
// "Argv" arguments converted to Odin strings // "Argv" arguments converted to Odin strings
args := _alloc_command_line_arguments() args := _alloc_command_line_arguments()
@(require_results)
_alloc_command_line_arguments :: proc() -> []string { _alloc_command_line_arguments :: proc() -> []string {
res := make([]string, len(runtime.args__)) res := make([]string, len(runtime.args__))
for arg, i in runtime.args__ { for arg, i in runtime.args__ {
@@ -269,8 +298,8 @@ _alloc_command_line_arguments :: proc() -> []string {
return res return res
} }
@private @(private, require_results)
_stat :: proc(path: string) -> (OS_Stat, Errno) { _stat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -278,13 +307,13 @@ _stat :: proc(path: string) -> (OS_Stat, Errno) {
s: OS_Stat = --- s: OS_Stat = ---
res := _unix_stat(cstr, &s) res := _unix_stat(cstr, &s)
if res == -1 { if res == -1 {
return s, Errno(get_last_error()) return s, get_last_error()
} }
return s, ERROR_NONE return s, nil
} }
@private @(private, require_results)
_lstat :: proc(path: string) -> (OS_Stat, Errno) { _lstat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -292,55 +321,54 @@ _lstat :: proc(path: string) -> (OS_Stat, Errno) {
s: OS_Stat = --- s: OS_Stat = ---
res := _unix_lstat(cstr, &s) res := _unix_lstat(cstr, &s)
if res == -1 { if res == -1 {
return s, Errno(get_last_error()) return s, get_last_error()
} }
return s, ERROR_NONE return s, nil
} }
@private @(private, require_results)
_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) { _fstat :: proc(fd: Handle) -> (OS_Stat, Error) {
// deliberately uninitialized // deliberately uninitialized
s: OS_Stat = --- s: OS_Stat = ---
res := _unix_fstat(fd, &s) res := _unix_fstat(fd, &s)
if res == -1 { if res == -1 {
return s, Errno(get_last_error()) return s, get_last_error()
} }
return s, ERROR_NONE return s, nil
} }
@private @(private)
_fdopendir :: proc(fd: Handle) -> (Dir, Errno) { _fdopendir :: proc(fd: Handle) -> (Dir, Error) {
dirp := _unix_fdopendir(fd) dirp := _unix_fdopendir(fd)
if dirp == cast(Dir)nil { if dirp == cast(Dir)nil {
return nil, Errno(get_last_error()) return nil, get_last_error()
} }
return dirp, ERROR_NONE return dirp, nil
} }
@private @(private)
_closedir :: proc(dirp: Dir) -> Errno { _closedir :: proc(dirp: Dir) -> Error {
rc := _unix_closedir(dirp) rc := _unix_closedir(dirp)
if rc != 0 { if rc != 0 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
@private @(private)
_rewinddir :: proc(dirp: Dir) { _rewinddir :: proc(dirp: Dir) {
_unix_rewinddir(dirp) _unix_rewinddir(dirp)
} }
@private @(private, require_results)
_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) { _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Error, end_of_stream: bool) {
result: ^Dirent result: ^Dirent
rc := _unix_readdir_r(dirp, &entry, &result) rc := _unix_readdir_r(dirp, &entry, &result)
if rc != 0 { if rc != 0 {
err = Errno(get_last_error()) err = get_last_error()
return return
} }
err = ERROR_NONE
if result == nil { if result == nil {
end_of_stream = true end_of_stream = true
@@ -350,8 +378,8 @@ _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool)
return return
} }
@private @(private, require_results)
_readlink :: proc(path: string) -> (string, Errno) { _readlink :: proc(path: string) -> (string, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator) runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
path_cstr := strings.clone_to_cstring(path, context.temp_allocator) path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -361,22 +389,24 @@ _readlink :: proc(path: string) -> (string, Errno) {
rc := _unix_readlink(path_cstr, &(buf[0]), bufsz) rc := _unix_readlink(path_cstr, &(buf[0]), bufsz)
if rc == -1 { if rc == -1 {
delete(buf) delete(buf)
return "", Errno(get_last_error()) return "", get_last_error()
} else if rc == int(bufsz) { } else if rc == int(bufsz) {
bufsz += MAX_PATH bufsz += MAX_PATH
delete(buf) delete(buf)
buf = make([]byte, bufsz) buf = make([]byte, bufsz)
} else { } else {
return strings.string_from_ptr(&buf[0], rc), ERROR_NONE return strings.string_from_ptr(&buf[0], rc), nil
} }
} }
} }
absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) { @(require_results)
return "", Errno(ENOSYS) absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {
return "", Error(ENOSYS)
} }
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) { @(require_results)
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
rel := rel rel := rel
if rel == "" { if rel == "" {
rel = "." rel = "."
@@ -387,26 +417,27 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
path_ptr := _unix_realpath(rel_cstr, nil) path_ptr := _unix_realpath(rel_cstr, nil)
if path_ptr == nil { if path_ptr == nil {
return "", Errno(get_last_error()) return "", get_last_error()
} }
defer _unix_free(path_ptr) defer _unix_free(path_ptr)
path_cstr := transmute(cstring)path_ptr path_cstr := cstring(path_ptr)
path = strings.clone( string(path_cstr) ) path = strings.clone(string(path_cstr))
return path, ERROR_NONE return path, nil
} }
access :: proc(path: string, mask: int) -> (bool, Errno) { access :: proc(path: string, mask: int) -> (bool, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_access(cstr, c.int(mask)) res := _unix_access(cstr, c.int(mask))
if res == -1 { if res == -1 {
return false, Errno(get_last_error()) return false, get_last_error()
} }
return true, ERROR_NONE return true, nil
} }
@(require_results)
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
path_str := strings.clone_to_cstring(key, context.temp_allocator) path_str := strings.clone_to_cstring(key, context.temp_allocator)
@@ -417,12 +448,13 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
return strings.clone(string(cstr), allocator), true return strings.clone(string(cstr), allocator), true
} }
@(require_results)
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) { get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
value, _ = lookup_env(key, allocator) value, _ = lookup_env(key, allocator)
return return
} }
@(private) @(private, require_results)
_processor_core_count :: proc() -> int { _processor_core_count :: proc() -> int {
info: haiku.system_info info: haiku.system_info
haiku.get_system_info(&info) haiku.get_system_info(&info)
+105 -60
View File
@@ -3,42 +3,45 @@ package os
import "base:runtime" import "base:runtime"
@(require_results)
is_path_separator :: proc(c: byte) -> bool { is_path_separator :: proc(c: byte) -> bool {
return c == '/' || c == '\\' return c == '/' || c == '\\'
} }
open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) { @(require_results)
open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
close :: proc(fd: Handle) -> Errno { close :: proc(fd: Handle) -> Error {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
flush :: proc(fd: Handle) -> (err: Errno) { flush :: proc(fd: Handle) -> (err: Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
write :: proc(fd: Handle, data: []byte) -> (int, Errno) { write :: proc(fd: Handle, data: []byte) -> (int, Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
@(private="file") @(private="file")
read_console :: proc(handle: Handle, b: []byte) -> (n: int, err: Errno) { read_console :: proc(handle: Handle, b: []byte) -> (n: int, err: Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
read :: proc(fd: Handle, data: []byte) -> (int, Errno) { read :: proc(fd: Handle, data: []byte) -> (int, Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
file_size :: proc(fd: Handle) -> (i64, Errno) { @(require_results)
file_size :: proc(fd: Handle) -> (i64, Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
@@ -47,38 +50,42 @@ file_size :: proc(fd: Handle) -> (i64, Errno) {
MAX_RW :: 1<<30 MAX_RW :: 1<<30
@(private) @(private)
pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) { pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
@(private) @(private)
pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) { pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Errno) { read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Errno) { write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
stdout: Handle = 1 stdout: Handle = 1
stderr: Handle = 2 stderr: Handle = 2
@(require_results)
get_std_handle :: proc "contextless" (h: uint) -> Handle { get_std_handle :: proc "contextless" (h: uint) -> Handle {
context = runtime.default_context() context = runtime.default_context()
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
@(require_results)
exists :: proc(path: string) -> bool { exists :: proc(path: string) -> bool {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
@(require_results)
is_file :: proc(path: string) -> bool { is_file :: proc(path: string) -> bool {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
@(require_results)
is_dir :: proc(path: string) -> bool { is_dir :: proc(path: string) -> bool {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
@@ -86,82 +93,118 @@ is_dir :: proc(path: string) -> bool {
// NOTE(tetra): GetCurrentDirectory is not thread safe with SetCurrentDirectory and GetFullPathName // NOTE(tetra): GetCurrentDirectory is not thread safe with SetCurrentDirectory and GetFullPathName
//@private cwd_lock := win32.SRWLOCK{} // zero is initialized //@private cwd_lock := win32.SRWLOCK{} // zero is initialized
@(require_results)
get_current_directory :: proc(allocator := context.allocator) -> string { get_current_directory :: proc(allocator := context.allocator) -> string {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
set_current_directory :: proc(path: string) -> (err: Errno) { set_current_directory :: proc(path: string) -> (err: Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
change_directory :: proc(path: string) -> (err: Errno) { change_directory :: proc(path: string) -> (err: Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
make_directory :: proc(path: string, mode: u32 = 0) -> (err: Errno) { make_directory :: proc(path: string, mode: u32 = 0) -> (err: Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
remove_directory :: proc(path: string) -> (err: Errno) { remove_directory :: proc(path: string) -> (err: Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
@(private) @(private, require_results)
is_abs :: proc(path: string) -> bool { is_abs :: proc(path: string) -> bool {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
@(private) @(private, require_results)
fix_long_path :: proc(path: string) -> string { fix_long_path :: proc(path: string) -> string {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
link :: proc(old_name, new_name: string) -> (err: Errno) { link :: proc(old_name, new_name: string) -> (err: Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
unlink :: proc(path: string) -> (err: Errno) { unlink :: proc(path: string) -> (err: Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
rename :: proc(old_path, new_path: string) -> (err: Errno) { rename :: proc(old_path, new_path: string) -> (err: Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
ftruncate :: proc(fd: Handle, length: i64) -> (err: Errno) { ftruncate :: proc(fd: Handle, length: i64) -> (err: Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
truncate :: proc(path: string, length: i64) -> (err: Errno) { truncate :: proc(path: string, length: i64) -> (err: Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
remove :: proc(name: string) -> Errno { remove :: proc(name: string) -> Error {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
pipe :: proc() -> (r, w: Handle, err: Errno) { @(require_results)
pipe :: proc() -> (r, w: Handle, err: Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) { @(require_results)
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
Handle :: distinct uintptr Handle :: distinct uintptr
File_Time :: distinct u64 File_Time :: distinct u64
Errno :: distinct int
_Platform_Error :: enum i32 {
NONE = 0,
FILE_NOT_FOUND = 2,
PATH_NOT_FOUND = 3,
ACCESS_DENIED = 5,
INVALID_HANDLE = 6,
NOT_ENOUGH_MEMORY = 8,
NO_MORE_FILES = 18,
HANDLE_EOF = 38,
NETNAME_DELETED = 64,
FILE_EXISTS = 80,
INVALID_PARAMETER = 87,
BROKEN_PIPE = 109,
BUFFER_OVERFLOW = 111,
INSUFFICIENT_BUFFER = 122,
MOD_NOT_FOUND = 126,
PROC_NOT_FOUND = 127,
DIR_NOT_EMPTY = 145,
ALREADY_EXISTS = 183,
ENVVAR_NOT_FOUND = 203,
MORE_DATA = 234,
OPERATION_ABORTED = 995,
IO_PENDING = 997,
NOT_FOUND = 1168,
PRIVILEGE_NOT_HELD = 1314,
WSAEACCES = 10013,
WSAECONNRESET = 10054,
// Windows reserves errors >= 1<<29 for application use
FILE_IS_PIPE = 1<<29 + 0,
FILE_IS_NOT_DIR = 1<<29 + 1,
NEGATIVE_OFFSET = 1<<29 + 2,
}
INVALID_HANDLE :: ~Handle(0) INVALID_HANDLE :: ~Handle(0)
@@ -182,37 +225,34 @@ O_ASYNC :: 0x02000
O_CLOEXEC :: 0x80000 O_CLOEXEC :: 0x80000
ERROR_NONE: Errno : 0 ERROR_FILE_NOT_FOUND :: Platform_Error.FILE_NOT_FOUND
ERROR_FILE_NOT_FOUND: Errno : 2 ERROR_PATH_NOT_FOUND :: Platform_Error.PATH_NOT_FOUND
ERROR_PATH_NOT_FOUND: Errno : 3 ERROR_ACCESS_DENIED :: Platform_Error.ACCESS_DENIED
ERROR_ACCESS_DENIED: Errno : 5 ERROR_INVALID_HANDLE :: Platform_Error.INVALID_HANDLE
ERROR_INVALID_HANDLE: Errno : 6 ERROR_NOT_ENOUGH_MEMORY :: Platform_Error.NOT_ENOUGH_MEMORY
ERROR_NOT_ENOUGH_MEMORY: Errno : 8 ERROR_NO_MORE_FILES :: Platform_Error.NO_MORE_FILES
ERROR_NO_MORE_FILES: Errno : 18 ERROR_HANDLE_EOF :: Platform_Error.HANDLE_EOF
ERROR_HANDLE_EOF: Errno : 38 ERROR_NETNAME_DELETED :: Platform_Error.NETNAME_DELETED
ERROR_NETNAME_DELETED: Errno : 64 ERROR_FILE_EXISTS :: Platform_Error.FILE_EXISTS
ERROR_FILE_EXISTS: Errno : 80 ERROR_INVALID_PARAMETER :: Platform_Error.INVALID_PARAMETER
ERROR_INVALID_PARAMETER: Errno : 87 ERROR_BROKEN_PIPE :: Platform_Error.BROKEN_PIPE
ERROR_BROKEN_PIPE: Errno : 109 ERROR_BUFFER_OVERFLOW :: Platform_Error.BUFFER_OVERFLOW
ERROR_BUFFER_OVERFLOW: Errno : 111 ERROR_INSUFFICIENT_BUFFER :: Platform_Error.INSUFFICIENT_BUFFER
ERROR_INSUFFICIENT_BUFFER: Errno : 122 ERROR_MOD_NOT_FOUND :: Platform_Error.MOD_NOT_FOUND
ERROR_MOD_NOT_FOUND: Errno : 126 ERROR_PROC_NOT_FOUND :: Platform_Error.PROC_NOT_FOUND
ERROR_PROC_NOT_FOUND: Errno : 127 ERROR_DIR_NOT_EMPTY :: Platform_Error.DIR_NOT_EMPTY
ERROR_DIR_NOT_EMPTY: Errno : 145 ERROR_ALREADY_EXISTS :: Platform_Error.ALREADY_EXISTS
ERROR_ALREADY_EXISTS: Errno : 183 ERROR_ENVVAR_NOT_FOUND :: Platform_Error.ENVVAR_NOT_FOUND
ERROR_ENVVAR_NOT_FOUND: Errno : 203 ERROR_MORE_DATA :: Platform_Error.MORE_DATA
ERROR_MORE_DATA: Errno : 234 ERROR_OPERATION_ABORTED :: Platform_Error.OPERATION_ABORTED
ERROR_OPERATION_ABORTED: Errno : 995 ERROR_IO_PENDING :: Platform_Error.IO_PENDING
ERROR_IO_PENDING: Errno : 997 ERROR_NOT_FOUND :: Platform_Error.NOT_FOUND
ERROR_NOT_FOUND: Errno : 1168 ERROR_PRIVILEGE_NOT_HELD :: Platform_Error.PRIVILEGE_NOT_HELD
ERROR_PRIVILEGE_NOT_HELD: Errno : 1314 WSAEACCES :: Platform_Error.WSAEACCES
WSAEACCES: Errno : 10013 WSAECONNRESET :: Platform_Error.WSAECONNRESET
WSAECONNRESET: Errno : 10054
// Windows reserves errors >= 1<<29 for application use ERROR_FILE_IS_PIPE :: General_Error.File_Is_Pipe
ERROR_FILE_IS_PIPE: Errno : 1<<29 + 0 ERROR_FILE_IS_NOT_DIR :: General_Error.Not_Dir
ERROR_FILE_IS_NOT_DIR: Errno : 1<<29 + 1
ERROR_NEGATIVE_OFFSET: Errno : 1<<29 + 2
// "Argv" arguments converted to Odin strings // "Argv" arguments converted to Odin strings
args := _alloc_command_line_arguments() args := _alloc_command_line_arguments()
@@ -221,20 +261,23 @@ args := _alloc_command_line_arguments()
last_write_time :: proc(fd: Handle) -> (File_Time, Errno) { @(require_results)
last_write_time :: proc(fd: Handle) -> (File_Time, Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) { @(require_results)
last_write_time_by_name :: proc(name: string) -> (File_Time, Error) {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
@(require_results)
get_page_size :: proc() -> int { get_page_size :: proc() -> int {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
@(private) @(private, require_results)
_processor_core_count :: proc() -> int { _processor_core_count :: proc() -> int {
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
} }
@@ -246,6 +289,7 @@ exit :: proc "contextless" (code: int) -> ! {
@(require_results)
current_thread_id :: proc "contextless" () -> int { current_thread_id :: proc "contextless" () -> int {
context = runtime.default_context() context = runtime.default_context()
unimplemented("core:os procedure not supported on JS target") unimplemented("core:os procedure not supported on JS target")
@@ -253,6 +297,7 @@ current_thread_id :: proc "contextless" () -> int {
@(require_results)
_alloc_command_line_arguments :: proc() -> []string { _alloc_command_line_arguments :: proc() -> []string {
return nil return nil
} }
+289 -254
View File
@@ -20,148 +20,148 @@ import "base:intrinsics"
// all that about compatibility. But we don't want to push experimental changes // all that about compatibility. But we don't want to push experimental changes
// and have people's code break while it's still work in progress. // and have people's code break while it's still work in progress.
import unix "core:sys/unix" import unix "core:sys/unix"
import linux "core:sys/linux"
Handle :: distinct i32 Handle :: distinct i32
Pid :: distinct i32 Pid :: distinct i32
File_Time :: distinct u64 File_Time :: distinct u64
Errno :: distinct i32
Socket :: distinct int Socket :: distinct int
INVALID_HANDLE :: ~Handle(0) INVALID_HANDLE :: ~Handle(0)
ERROR_NONE: Errno : 0 _Platform_Error :: linux.Errno
EPERM: Errno : 1 EPERM :: Platform_Error.EPERM
ENOENT: Errno : 2 ENOENT :: Platform_Error.ENOENT
ESRCH: Errno : 3 ESRCH :: Platform_Error.ESRCH
EINTR: Errno : 4 EINTR :: Platform_Error.EINTR
EIO: Errno : 5 EIO :: Platform_Error.EIO
ENXIO: Errno : 6 ENXIO :: Platform_Error.ENXIO
EBADF: Errno : 9 EBADF :: Platform_Error.EBADF
EAGAIN: Errno : 11 EAGAIN :: Platform_Error.EAGAIN
ENOMEM: Errno : 12 ENOMEM :: Platform_Error.ENOMEM
EACCES: Errno : 13 EACCES :: Platform_Error.EACCES
EFAULT: Errno : 14 EFAULT :: Platform_Error.EFAULT
EEXIST: Errno : 17 EEXIST :: Platform_Error.EEXIST
ENODEV: Errno : 19 ENODEV :: Platform_Error.ENODEV
ENOTDIR: Errno : 20 ENOTDIR :: Platform_Error.ENOTDIR
EISDIR: Errno : 21 EISDIR :: Platform_Error.EISDIR
EINVAL: Errno : 22 EINVAL :: Platform_Error.EINVAL
ENFILE: Errno : 23 ENFILE :: Platform_Error.ENFILE
EMFILE: Errno : 24 EMFILE :: Platform_Error.EMFILE
ETXTBSY: Errno : 26 ETXTBSY :: Platform_Error.ETXTBSY
EFBIG: Errno : 27 EFBIG :: Platform_Error.EFBIG
ENOSPC: Errno : 28 ENOSPC :: Platform_Error.ENOSPC
ESPIPE: Errno : 29 ESPIPE :: Platform_Error.ESPIPE
EROFS: Errno : 30 EROFS :: Platform_Error.EROFS
EPIPE: Errno : 32 EPIPE :: Platform_Error.EPIPE
ERANGE: Errno : 34 /* Result too large */ ERANGE :: Platform_Error.ERANGE /* Result too large */
EDEADLK: Errno : 35 /* Resource deadlock would occur */ EDEADLK :: Platform_Error.EDEADLK /* Resource deadlock would occur */
ENAMETOOLONG: Errno : 36 /* File name too long */ ENAMETOOLONG :: Platform_Error.ENAMETOOLONG /* File name too long */
ENOLCK: Errno : 37 /* No record locks available */ ENOLCK :: Platform_Error.ENOLCK /* No record locks available */
ENOSYS: Errno : 38 /* Invalid system call number */ ENOSYS :: Platform_Error.ENOSYS /* Invalid system call number */
ENOTEMPTY: Errno : 39 /* Directory not empty */ ENOTEMPTY :: Platform_Error.ENOTEMPTY /* Directory not empty */
ELOOP: Errno : 40 /* Too many symbolic links encountered */ ELOOP :: Platform_Error.ELOOP /* Too many symbolic links encountered */
EWOULDBLOCK: Errno : EAGAIN /* Operation would block */ EWOULDBLOCK :: Platform_Error.EWOULDBLOCK /* Operation would block */
ENOMSG: Errno : 42 /* No message of desired type */ ENOMSG :: Platform_Error.ENOMSG /* No message of desired type */
EIDRM: Errno : 43 /* Identifier removed */ EIDRM :: Platform_Error.EIDRM /* Identifier removed */
ECHRNG: Errno : 44 /* Channel number out of range */ ECHRNG :: Platform_Error.ECHRNG /* Channel number out of range */
EL2NSYNC: Errno : 45 /* Level 2 not synchronized */ EL2NSYNC :: Platform_Error.EL2NSYNC /* Level 2 not synchronized */
EL3HLT: Errno : 46 /* Level 3 halted */ EL3HLT :: Platform_Error.EL3HLT /* Level 3 halted */
EL3RST: Errno : 47 /* Level 3 reset */ EL3RST :: Platform_Error.EL3RST /* Level 3 reset */
ELNRNG: Errno : 48 /* Link number out of range */ ELNRNG :: Platform_Error.ELNRNG /* Link number out of range */
EUNATCH: Errno : 49 /* Protocol driver not attached */ EUNATCH :: Platform_Error.EUNATCH /* Protocol driver not attached */
ENOCSI: Errno : 50 /* No CSI structure available */ ENOCSI :: Platform_Error.ENOCSI /* No CSI structure available */
EL2HLT: Errno : 51 /* Level 2 halted */ EL2HLT :: Platform_Error.EL2HLT /* Level 2 halted */
EBADE: Errno : 52 /* Invalid exchange */ EBADE :: Platform_Error.EBADE /* Invalid exchange */
EBADR: Errno : 53 /* Invalid request descriptor */ EBADR :: Platform_Error.EBADR /* Invalid request descriptor */
EXFULL: Errno : 54 /* Exchange full */ EXFULL :: Platform_Error.EXFULL /* Exchange full */
ENOANO: Errno : 55 /* No anode */ ENOANO :: Platform_Error.ENOANO /* No anode */
EBADRQC: Errno : 56 /* Invalid request code */ EBADRQC :: Platform_Error.EBADRQC /* Invalid request code */
EBADSLT: Errno : 57 /* Invalid slot */ EBADSLT :: Platform_Error.EBADSLT /* Invalid slot */
EDEADLOCK: Errno : EDEADLK EDEADLOCK :: Platform_Error.EDEADLOCK
EBFONT: Errno : 59 /* Bad font file format */ EBFONT :: Platform_Error.EBFONT /* Bad font file format */
ENOSTR: Errno : 60 /* Device not a stream */ ENOSTR :: Platform_Error.ENOSTR /* Device not a stream */
ENODATA: Errno : 61 /* No data available */ ENODATA :: Platform_Error.ENODATA /* No data available */
ETIME: Errno : 62 /* Timer expired */ ETIME :: Platform_Error.ETIME /* Timer expired */
ENOSR: Errno : 63 /* Out of streams resources */ ENOSR :: Platform_Error.ENOSR /* Out of streams resources */
ENONET: Errno : 64 /* Machine is not on the network */ ENONET :: Platform_Error.ENONET /* Machine is not on the network */
ENOPKG: Errno : 65 /* Package not installed */ ENOPKG :: Platform_Error.ENOPKG /* Package not installed */
EREMOTE: Errno : 66 /* Object is remote */ EREMOTE :: Platform_Error.EREMOTE /* Object is remote */
ENOLINK: Errno : 67 /* Link has been severed */ ENOLINK :: Platform_Error.ENOLINK /* Link has been severed */
EADV: Errno : 68 /* Advertise error */ EADV :: Platform_Error.EADV /* Advertise error */
ESRMNT: Errno : 69 /* Srmount error */ ESRMNT :: Platform_Error.ESRMNT /* Srmount error */
ECOMM: Errno : 70 /* Communication error on send */ ECOMM :: Platform_Error.ECOMM /* Communication error on send */
EPROTO: Errno : 71 /* Protocol error */ EPROTO :: Platform_Error.EPROTO /* Protocol error */
EMULTIHOP: Errno : 72 /* Multihop attempted */ EMULTIHOP :: Platform_Error.EMULTIHOP /* Multihop attempted */
EDOTDOT: Errno : 73 /* RFS specific error */ EDOTDOT :: Platform_Error.EDOTDOT /* RFS specific error */
EBADMSG: Errno : 74 /* Not a data message */ EBADMSG :: Platform_Error.EBADMSG /* Not a data message */
EOVERFLOW: Errno : 75 /* Value too large for defined data type */ EOVERFLOW :: Platform_Error.EOVERFLOW /* Value too large for defined data type */
ENOTUNIQ: Errno : 76 /* Name not unique on network */ ENOTUNIQ :: Platform_Error.ENOTUNIQ /* Name not unique on network */
EBADFD: Errno : 77 /* File descriptor in bad state */ EBADFD :: Platform_Error.EBADFD /* File descriptor in bad state */
EREMCHG: Errno : 78 /* Remote address changed */ EREMCHG :: Platform_Error.EREMCHG /* Remote address changed */
ELIBACC: Errno : 79 /* Can not access a needed shared library */ ELIBACC :: Platform_Error.ELIBACC /* Can not access a needed shared library */
ELIBBAD: Errno : 80 /* Accessing a corrupted shared library */ ELIBBAD :: Platform_Error.ELIBBAD /* Accessing a corrupted shared library */
ELIBSCN: Errno : 81 /* .lib section in a.out corrupted */ ELIBSCN :: Platform_Error.ELIBSCN /* .lib section in a.out corrupted */
ELIBMAX: Errno : 82 /* Attempting to link in too many shared libraries */ ELIBMAX :: Platform_Error.ELIBMAX /* Attempting to link in too many shared libraries */
ELIBEXEC: Errno : 83 /* Cannot exec a shared library directly */ ELIBEXEC :: Platform_Error.ELIBEXEC /* Cannot exec a shared library directly */
EILSEQ: Errno : 84 /* Illegal byte sequence */ EILSEQ :: Platform_Error.EILSEQ /* Illegal byte sequence */
ERESTART: Errno : 85 /* Interrupted system call should be restarted */ ERESTART :: Platform_Error.ERESTART /* Interrupted system call should be restarted */
ESTRPIPE: Errno : 86 /* Streams pipe error */ ESTRPIPE :: Platform_Error.ESTRPIPE /* Streams pipe error */
EUSERS: Errno : 87 /* Too many users */ EUSERS :: Platform_Error.EUSERS /* Too many users */
ENOTSOCK: Errno : 88 /* Socket operation on non-socket */ ENOTSOCK :: Platform_Error.ENOTSOCK /* Socket operation on non-socket */
EDESTADDRREQ: Errno : 89 /* Destination address required */ EDESTADDRREQ :: Platform_Error.EDESTADDRREQ /* Destination address required */
EMSGSIZE: Errno : 90 /* Message too long */ EMSGSIZE :: Platform_Error.EMSGSIZE /* Message too long */
EPROTOTYPE: Errno : 91 /* Protocol wrong type for socket */ EPROTOTYPE :: Platform_Error.EPROTOTYPE /* Protocol wrong type for socket */
ENOPROTOOPT: Errno : 92 /* Protocol not available */ ENOPROTOOPT :: Platform_Error.ENOPROTOOPT /* Protocol not available */
EPROTONOSUPPORT:Errno : 93 /* Protocol not supported */ EPROTONOSUPPOR :: Platform_Error.EPROTONOSUPPORT /* Protocol not supported */
ESOCKTNOSUPPORT:Errno : 94 /* Socket type not supported */ ESOCKTNOSUPPOR :: Platform_Error.ESOCKTNOSUPPORT /* Socket type not supported */
EOPNOTSUPP: Errno : 95 /* Operation not supported on transport endpoint */ EOPNOTSUPP :: Platform_Error.EOPNOTSUPP /* Operation not supported on transport endpoint */
EPFNOSUPPORT: Errno : 96 /* Protocol family not supported */ EPFNOSUPPORT :: Platform_Error.EPFNOSUPPORT /* Protocol family not supported */
EAFNOSUPPORT: Errno : 97 /* Address family not supported by protocol */ EAFNOSUPPORT :: Platform_Error.EAFNOSUPPORT /* Address family not supported by protocol */
EADDRINUSE: Errno : 98 /* Address already in use */ EADDRINUSE :: Platform_Error.EADDRINUSE /* Address already in use */
EADDRNOTAVAIL: Errno : 99 /* Cannot assign requested address */ EADDRNOTAVAIL :: Platform_Error.EADDRNOTAVAIL /* Cannot assign requested address */
ENETDOWN: Errno : 100 /* Network is down */ ENETDOWN :: Platform_Error.ENETDOWN /* Network is down */
ENETUNREACH: Errno : 101 /* Network is unreachable */ ENETUNREACH :: Platform_Error.ENETUNREACH /* Network is unreachable */
ENETRESET: Errno : 102 /* Network dropped connection because of reset */ ENETRESET :: Platform_Error.ENETRESET /* Network dropped connection because of reset */
ECONNABORTED: Errno : 103 /* Software caused connection abort */ ECONNABORTED :: Platform_Error.ECONNABORTED /* Software caused connection abort */
ECONNRESET: Errno : 104 /* Connection reset by peer */ ECONNRESET :: Platform_Error.ECONNRESET /* Connection reset by peer */
ENOBUFS: Errno : 105 /* No buffer space available */ ENOBUFS :: Platform_Error.ENOBUFS /* No buffer space available */
EISCONN: Errno : 106 /* Transport endpoint is already connected */ EISCONN :: Platform_Error.EISCONN /* Transport endpoint is already connected */
ENOTCONN: Errno : 107 /* Transport endpoint is not connected */ ENOTCONN :: Platform_Error.ENOTCONN /* Transport endpoint is not connected */
ESHUTDOWN: Errno : 108 /* Cannot send after transport endpoint shutdown */ ESHUTDOWN :: Platform_Error.ESHUTDOWN /* Cannot send after transport endpoint shutdown */
ETOOMANYREFS: Errno : 109 /* Too many references: cannot splice */ ETOOMANYREFS :: Platform_Error.ETOOMANYREFS /* Too many references: cannot splice */
ETIMEDOUT: Errno : 110 /* Connection timed out */ ETIMEDOUT :: Platform_Error.ETIMEDOUT /* Connection timed out */
ECONNREFUSED: Errno : 111 /* Connection refused */ ECONNREFUSED :: Platform_Error.ECONNREFUSED /* Connection refused */
EHOSTDOWN: Errno : 112 /* Host is down */ EHOSTDOWN :: Platform_Error.EHOSTDOWN /* Host is down */
EHOSTUNREACH: Errno : 113 /* No route to host */ EHOSTUNREACH :: Platform_Error.EHOSTUNREACH /* No route to host */
EALREADY: Errno : 114 /* Operation already in progress */ EALREADY :: Platform_Error.EALREADY /* Operation already in progress */
EINPROGRESS: Errno : 115 /* Operation now in progress */ EINPROGRESS :: Platform_Error.EINPROGRESS /* Operation now in progress */
ESTALE: Errno : 116 /* Stale file handle */ ESTALE :: Platform_Error.ESTALE /* Stale file handle */
EUCLEAN: Errno : 117 /* Structure needs cleaning */ EUCLEAN :: Platform_Error.EUCLEAN /* Structure needs cleaning */
ENOTNAM: Errno : 118 /* Not a XENIX named type file */ ENOTNAM :: Platform_Error.ENOTNAM /* Not a XENIX named type file */
ENAVAIL: Errno : 119 /* No XENIX semaphores available */ ENAVAIL :: Platform_Error.ENAVAIL /* No XENIX semaphores available */
EISNAM: Errno : 120 /* Is a named type file */ EISNAM :: Platform_Error.EISNAM /* Is a named type file */
EREMOTEIO: Errno : 121 /* Remote I/O error */ EREMOTEIO :: Platform_Error.EREMOTEIO /* Remote I/O error */
EDQUOT: Errno : 122 /* Quota exceeded */ EDQUOT :: Platform_Error.EDQUOT /* Quota exceeded */
ENOMEDIUM: Errno : 123 /* No medium found */ ENOMEDIUM :: Platform_Error.ENOMEDIUM /* No medium found */
EMEDIUMTYPE: Errno : 124 /* Wrong medium type */ EMEDIUMTYPE :: Platform_Error.EMEDIUMTYPE /* Wrong medium type */
ECANCELED: Errno : 125 /* Operation Canceled */ ECANCELED :: Platform_Error.ECANCELED /* Operation Canceled */
ENOKEY: Errno : 126 /* Required key not available */ ENOKEY :: Platform_Error.ENOKEY /* Required key not available */
EKEYEXPIRED: Errno : 127 /* Key has expired */ EKEYEXPIRED :: Platform_Error.EKEYEXPIRED /* Key has expired */
EKEYREVOKED: Errno : 128 /* Key has been revoked */ EKEYREVOKED :: Platform_Error.EKEYREVOKED /* Key has been revoked */
EKEYREJECTED: Errno : 129 /* Key was rejected by service */ EKEYREJECTED :: Platform_Error.EKEYREJECTED /* Key was rejected by service */
/* for robust mutexes */ /* for robust mutexes */
EOWNERDEAD: Errno : 130 /* Owner died */ EOWNERDEAD :: Platform_Error.EOWNERDEAD /* Owner died */
ENOTRECOVERABLE: Errno : 131 /* State not recoverable */ ENOTRECOVERABLE :: Platform_Error.ENOTRECOVERABLE /* State not recoverable */
ERFKILL: Errno : 132 /* Operation not possible due to RF-kill */ ERFKILL :: Platform_Error.ERFKILL /* Operation not possible due to RF-kill */
EHWPOISON: Errno : 133 /* Memory page has hardware error */ EHWPOISON :: Platform_Error.EHWPOISON /* Memory page has hardware error */
ADDR_NO_RANDOMIZE :: 0x40000 ADDR_NO_RANDOMIZE :: 0x40000
@@ -448,13 +448,13 @@ S_ISGID :: 0o2000 // Set group id on execution
S_ISVTX :: 0o1000 // Directory restrcted delete S_ISVTX :: 0o1000 // Directory restrcted delete
S_ISLNK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFLNK } @(require_results) S_ISLNK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFLNK }
S_ISREG :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFREG } @(require_results) S_ISREG :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFREG }
S_ISDIR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFDIR } @(require_results) S_ISDIR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFDIR }
S_ISCHR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFCHR } @(require_results) S_ISCHR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFCHR }
S_ISBLK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFBLK } @(require_results) S_ISBLK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFBLK }
S_ISFIFO :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFIFO } @(require_results) S_ISFIFO :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFIFO }
S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK } @(require_results) S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK }
F_OK :: 0 // Test for file existance F_OK :: 0 // Test for file existance
X_OK :: 1 // Test for execute permission X_OK :: 1 // Test for execute permission
@@ -506,41 +506,55 @@ foreign dl {
@(link_name="freeifaddrs") _freeifaddrs :: proc(ifa: ^ifaddrs) --- @(link_name="freeifaddrs") _freeifaddrs :: proc(ifa: ^ifaddrs) ---
} }
@(require_results)
is_path_separator :: proc(r: rune) -> bool { is_path_separator :: proc(r: rune) -> bool {
return r == '/' return r == '/'
} }
// determine errno from syscall return value // determine errno from syscall return value
@private @(private, require_results)
_get_errno :: proc(res: int) -> Errno { _get_errno :: proc(res: int) -> Error {
if res < 0 && res > -4096 { if res < 0 && res > -4096 {
return Errno(-res) return Platform_Error(-res)
} }
return 0 return nil
} }
// get errno from libc // get errno from libc
get_last_error :: proc "contextless" () -> int { @(require_results, no_instrumentation)
return int(__errno_location()^) get_last_error :: proc "contextless" () -> Error {
err := Platform_Error(__errno_location()^)
#partial switch err {
case .NONE:
return nil
case .EPERM:
return .Permission_Denied
case .EEXIST:
return .Exist
case .ENOENT:
return .Not_Exist
}
return err
} }
personality :: proc(persona: u64) -> (Errno) { personality :: proc(persona: u64) -> Error {
res := unix.sys_personality(persona) res := unix.sys_personality(persona)
if res == -1 { if res == -1 {
return _get_errno(res) return _get_errno(res)
} }
return ERROR_NONE return nil
} }
fork :: proc() -> (Pid, Errno) { @(require_results)
fork :: proc() -> (Pid, Error) {
pid := unix.sys_fork() pid := unix.sys_fork()
if pid == -1 { if pid == -1 {
return -1, _get_errno(pid) return -1, _get_errno(pid)
} }
return Pid(pid), ERROR_NONE return Pid(pid), nil
} }
execvp :: proc(path: string, args: []string) -> Errno { execvp :: proc(path: string, args: []string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator) path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -551,24 +565,30 @@ execvp :: proc(path: string, args: []string) -> Errno {
} }
_unix_execvp(path_cstr, raw_data(args_cstrs)) _unix_execvp(path_cstr, raw_data(args_cstrs))
return Errno(get_last_error()) return get_last_error()
} }
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0o000) -> (Handle, Errno) { @(require_results)
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0o000) -> (Handle, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
handle := unix.sys_open(cstr, flags, uint(mode)) handle := unix.sys_open(cstr, flags, uint(mode))
if handle < 0 { if handle < 0 {
return INVALID_HANDLE, _get_errno(handle) return INVALID_HANDLE, _get_errno(handle)
} }
return Handle(handle), ERROR_NONE return Handle(handle), nil
} }
close :: proc(fd: Handle) -> Errno { close :: proc(fd: Handle) -> Error {
return _get_errno(unix.sys_close(int(fd))) return _get_errno(unix.sys_close(int(fd)))
} }
flush :: proc(fd: Handle) -> Error {
// do nothing
return nil
}
// If you read or write more than `SSIZE_MAX` bytes, result is implementation defined (probably an error). // If you read or write more than `SSIZE_MAX` bytes, result is implementation defined (probably an error).
// `SSIZE_MAX` is also implementation defined but usually the max of a `ssize_t` which is `max(int)` in Odin. // `SSIZE_MAX` is also implementation defined but usually the max of a `ssize_t` which is `max(int)` in Odin.
// In practice a read/write call would probably never read/write these big buffers all at once, // In practice a read/write call would probably never read/write these big buffers all at once,
@@ -578,9 +598,9 @@ close :: proc(fd: Handle) -> Errno {
@(private) @(private)
MAX_RW :: 1 << 30 MAX_RW :: 1 << 30
read :: proc(fd: Handle, data: []byte) -> (int, Errno) { read :: proc(fd: Handle, data: []byte) -> (int, Error) {
if len(data) == 0 { if len(data) == 0 {
return 0, ERROR_NONE return 0, nil
} }
to_read := min(uint(len(data)), MAX_RW) to_read := min(uint(len(data)), MAX_RW)
@@ -589,12 +609,12 @@ read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
if bytes_read < 0 { if bytes_read < 0 {
return -1, _get_errno(bytes_read) return -1, _get_errno(bytes_read)
} }
return bytes_read, ERROR_NONE return bytes_read, nil
} }
write :: proc(fd: Handle, data: []byte) -> (int, Errno) { write :: proc(fd: Handle, data: []byte) -> (int, Error) {
if len(data) == 0 { if len(data) == 0 {
return 0, ERROR_NONE return 0, nil
} }
to_write := min(uint(len(data)), MAX_RW) to_write := min(uint(len(data)), MAX_RW)
@@ -603,12 +623,12 @@ write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
if bytes_written < 0 { if bytes_written < 0 {
return -1, _get_errno(bytes_written) return -1, _get_errno(bytes_written)
} }
return bytes_written, ERROR_NONE return bytes_written, nil
} }
read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) { read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Error) {
if len(data) == 0 { if len(data) == 0 {
return 0, ERROR_NONE return 0, nil
} }
to_read := min(uint(len(data)), MAX_RW) to_read := min(uint(len(data)), MAX_RW)
@@ -617,12 +637,12 @@ read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
if bytes_read < 0 { if bytes_read < 0 {
return -1, _get_errno(bytes_read) return -1, _get_errno(bytes_read)
} }
return bytes_read, ERROR_NONE return bytes_read, nil
} }
write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) { write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Error) {
if len(data) == 0 { if len(data) == 0 {
return 0, ERROR_NONE return 0, nil
} }
to_write := min(uint(len(data)), MAX_RW) to_write := min(uint(len(data)), MAX_RW)
@@ -631,92 +651,97 @@ write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
if bytes_written < 0 { if bytes_written < 0 {
return -1, _get_errno(bytes_written) return -1, _get_errno(bytes_written)
} }
return bytes_written, ERROR_NONE return bytes_written, nil
} }
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
res := unix.sys_lseek(int(fd), offset, whence) res := unix.sys_lseek(int(fd), offset, whence)
if res < 0 { if res < 0 {
return -1, _get_errno(int(res)) return -1, _get_errno(int(res))
} }
return i64(res), ERROR_NONE return i64(res), nil
} }
file_size :: proc(fd: Handle) -> (i64, Errno) { @(require_results)
file_size :: proc(fd: Handle) -> (i64, Error) {
// deliberately uninitialized; the syscall fills this buffer for us // deliberately uninitialized; the syscall fills this buffer for us
s: OS_Stat = --- s: OS_Stat = ---
result := unix.sys_fstat(int(fd), rawptr(&s)) result := unix.sys_fstat(int(fd), rawptr(&s))
if result < 0 { if result < 0 {
return 0, _get_errno(result) return 0, _get_errno(result)
} }
return max(s.size, 0), ERROR_NONE return max(s.size, 0), nil
} }
rename :: proc(old_path, new_path: string) -> Errno { rename :: proc(old_path, new_path: string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator) old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator)
new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator) new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator)
return _get_errno(unix.sys_rename(old_path_cstr, new_path_cstr)) return _get_errno(unix.sys_rename(old_path_cstr, new_path_cstr))
} }
remove :: proc(path: string) -> Errno { remove :: proc(path: string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator) path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
return _get_errno(unix.sys_unlink(path_cstr)) return _get_errno(unix.sys_unlink(path_cstr))
} }
make_directory :: proc(path: string, mode: u32 = 0o775) -> Errno { make_directory :: proc(path: string, mode: u32 = 0o775) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator) path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
return _get_errno(unix.sys_mkdir(path_cstr, uint(mode))) return _get_errno(unix.sys_mkdir(path_cstr, uint(mode)))
} }
remove_directory :: proc(path: string) -> Errno { remove_directory :: proc(path: string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator) path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
return _get_errno(unix.sys_rmdir(path_cstr)) return _get_errno(unix.sys_rmdir(path_cstr))
} }
@(require_results)
is_file_handle :: proc(fd: Handle) -> bool { is_file_handle :: proc(fd: Handle) -> bool {
s, err := _fstat(fd) s, err := _fstat(fd)
if err != ERROR_NONE { if err != nil {
return false return false
} }
return S_ISREG(s.mode) return S_ISREG(s.mode)
} }
@(require_results)
is_file_path :: proc(path: string, follow_links: bool = true) -> bool { is_file_path :: proc(path: string, follow_links: bool = true) -> bool {
s: OS_Stat s: OS_Stat
err: Errno err: Error
if follow_links { if follow_links {
s, err = _stat(path) s, err = _stat(path)
} else { } else {
s, err = _lstat(path) s, err = _lstat(path)
} }
if err != ERROR_NONE { if err != nil {
return false return false
} }
return S_ISREG(s.mode) return S_ISREG(s.mode)
} }
@(require_results)
is_dir_handle :: proc(fd: Handle) -> bool { is_dir_handle :: proc(fd: Handle) -> bool {
s, err := _fstat(fd) s, err := _fstat(fd)
if err != ERROR_NONE { if err != nil {
return false return false
} }
return S_ISDIR(s.mode) return S_ISDIR(s.mode)
} }
@(require_results)
is_dir_path :: proc(path: string, follow_links: bool = true) -> bool { is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
s: OS_Stat s: OS_Stat
err: Errno err: Error
if follow_links { if follow_links {
s, err = _stat(path) s, err = _stat(path)
} else { } else {
s, err = _lstat(path) s, err = _lstat(path)
} }
if err != ERROR_NONE { if err != nil {
return false return false
} }
return S_ISDIR(s.mode) return S_ISDIR(s.mode)
@@ -725,6 +750,7 @@ is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
is_file :: proc {is_file_path, is_file_handle} is_file :: proc {is_file_path, is_file_handle}
is_dir :: proc {is_dir_path, is_dir_handle} is_dir :: proc {is_dir_path, is_dir_handle}
@(require_results)
exists :: proc(path: string) -> bool { exists :: proc(path: string) -> bool {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cpath := strings.clone_to_cstring(path, context.temp_allocator) cpath := strings.clone_to_cstring(path, context.temp_allocator)
@@ -742,26 +768,22 @@ stderr: Handle = 2
last_write_time :: proc(fd: Handle) -> File_Time {} last_write_time :: proc(fd: Handle) -> File_Time {}
last_write_time_by_name :: proc(name: string) -> File_Time {} last_write_time_by_name :: proc(name: string) -> File_Time {}
*/ */
last_write_time :: proc(fd: Handle) -> (File_Time, Errno) { @(require_results)
s, err := _fstat(fd) last_write_time :: proc(fd: Handle) -> (time: File_Time, err: Error) {
if err != ERROR_NONE { s := _fstat(fd) or_return
return 0, err
}
modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
return File_Time(modified), ERROR_NONE return File_Time(modified), nil
} }
last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) { @(require_results)
s, err := _stat(name) last_write_time_by_name :: proc(name: string) -> (time: File_Time, err: Error) {
if err != ERROR_NONE { s := _stat(name) or_return
return 0, err
}
modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
return File_Time(modified), ERROR_NONE return File_Time(modified), nil
} }
@private @(private, require_results)
_stat :: proc(path: string) -> (OS_Stat, Errno) { _stat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -771,11 +793,11 @@ _stat :: proc(path: string) -> (OS_Stat, Errno) {
if result < 0 { if result < 0 {
return s, _get_errno(result) return s, _get_errno(result)
} }
return s, ERROR_NONE return s, nil
} }
@private @(private, require_results)
_lstat :: proc(path: string) -> (OS_Stat, Errno) { _lstat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -785,53 +807,53 @@ _lstat :: proc(path: string) -> (OS_Stat, Errno) {
if result < 0 { if result < 0 {
return s, _get_errno(result) return s, _get_errno(result)
} }
return s, ERROR_NONE return s, nil
} }
@private @(private, require_results)
_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) { _fstat :: proc(fd: Handle) -> (OS_Stat, Error) {
// deliberately uninitialized; the syscall fills this buffer for us // deliberately uninitialized; the syscall fills this buffer for us
s: OS_Stat = --- s: OS_Stat = ---
result := unix.sys_fstat(int(fd), rawptr(&s)) result := unix.sys_fstat(int(fd), rawptr(&s))
if result < 0 { if result < 0 {
return s, _get_errno(result) return s, _get_errno(result)
} }
return s, ERROR_NONE return s, nil
} }
@private @(private, require_results)
_fdopendir :: proc(fd: Handle) -> (Dir, Errno) { _fdopendir :: proc(fd: Handle) -> (Dir, Error) {
dirp := _unix_fdopendir(fd) dirp := _unix_fdopendir(fd)
if dirp == cast(Dir)nil { if dirp == cast(Dir)nil {
return nil, Errno(get_last_error()) return nil, get_last_error()
} }
return dirp, ERROR_NONE return dirp, nil
} }
@private @(private)
_closedir :: proc(dirp: Dir) -> Errno { _closedir :: proc(dirp: Dir) -> Error {
rc := _unix_closedir(dirp) rc := _unix_closedir(dirp)
if rc != 0 { if rc != 0 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
@private @(private)
_rewinddir :: proc(dirp: Dir) { _rewinddir :: proc(dirp: Dir) {
_unix_rewinddir(dirp) _unix_rewinddir(dirp)
} }
@private @(private, require_results)
_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) { _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Error, end_of_stream: bool) {
result: ^Dirent result: ^Dirent
rc := _unix_readdir_r(dirp, &entry, &result) rc := _unix_readdir_r(dirp, &entry, &result)
if rc != 0 { if rc != 0 {
err = Errno(get_last_error()) err = get_last_error()
return return
} }
err = ERROR_NONE err = nil
if result == nil { if result == nil {
end_of_stream = true end_of_stream = true
@@ -842,8 +864,8 @@ _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool)
return return
} }
@private @(private, require_results)
_readlink :: proc(path: string) -> (string, Errno) { _readlink :: proc(path: string) -> (string, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator) runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
path_cstr := strings.clone_to_cstring(path, context.temp_allocator) path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -860,12 +882,13 @@ _readlink :: proc(path: string) -> (string, Errno) {
delete(buf) delete(buf)
buf = make([]byte, bufsz) buf = make([]byte, bufsz)
} else { } else {
return strings.string_from_ptr(&buf[0], rc), ERROR_NONE return strings.string_from_ptr(&buf[0], rc), nil
} }
} }
} }
absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) { @(require_results)
absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {
buf : [256]byte buf : [256]byte
fd_str := strconv.itoa( buf[:], cast(int)fd ) fd_str := strconv.itoa( buf[:], cast(int)fd )
@@ -875,7 +898,8 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) {
return _readlink(procfs_path) return _readlink(procfs_path)
} }
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) { @(require_results)
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
rel := rel rel := rel
if rel == "" { if rel == "" {
rel = "." rel = "."
@@ -886,25 +910,26 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
path_ptr := _unix_realpath(rel_cstr, nil) path_ptr := _unix_realpath(rel_cstr, nil)
if path_ptr == nil { if path_ptr == nil {
return "", Errno(get_last_error()) return "", get_last_error()
} }
defer _unix_free(path_ptr) defer _unix_free(path_ptr)
path = strings.clone(string(cstring(path_ptr))) path = strings.clone(string(cstring(path_ptr)))
return path, ERROR_NONE return path, nil
} }
access :: proc(path: string, mask: int) -> (bool, Errno) { access :: proc(path: string, mask: int) -> (bool, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
result := unix.sys_access(cstr, mask) result := unix.sys_access(cstr, mask)
if result < 0 { if result < 0 {
return false, _get_errno(result) return false, _get_errno(result)
} }
return true, ERROR_NONE return true, nil
} }
@(require_results)
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
path_str := strings.clone_to_cstring(key, context.temp_allocator) path_str := strings.clone_to_cstring(key, context.temp_allocator)
@@ -916,33 +941,35 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
return strings.clone(string(cstr), allocator), true return strings.clone(string(cstr), allocator), true
} }
@(require_results)
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) { get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
value, _ = lookup_env(key, allocator) value, _ = lookup_env(key, allocator)
return return
} }
set_env :: proc(key, value: string) -> Errno { set_env :: proc(key, value: string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
key_cstring := strings.clone_to_cstring(key, context.temp_allocator) key_cstring := strings.clone_to_cstring(key, context.temp_allocator)
value_cstring := strings.clone_to_cstring(value, context.temp_allocator) value_cstring := strings.clone_to_cstring(value, context.temp_allocator)
// NOTE(GoNZooo): `setenv` instead of `putenv` because it copies both key and value more commonly // NOTE(GoNZooo): `setenv` instead of `putenv` because it copies both key and value more commonly
res := _unix_setenv(key_cstring, value_cstring, 1) res := _unix_setenv(key_cstring, value_cstring, 1)
if res < 0 { if res < 0 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
unset_env :: proc(key: string) -> Errno { unset_env :: proc(key: string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
s := strings.clone_to_cstring(key, context.temp_allocator) s := strings.clone_to_cstring(key, context.temp_allocator)
res := _unix_putenv(s) res := _unix_putenv(s)
if res < 0 { if res < 0 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
@(require_results)
get_current_directory :: proc() -> string { get_current_directory :: proc() -> string {
// NOTE(tetra): I would use PATH_MAX here, but I was not able to find // NOTE(tetra): I would use PATH_MAX here, but I was not able to find
// an authoritative value for it across all systems. // an authoritative value for it across all systems.
@@ -964,14 +991,14 @@ get_current_directory :: proc() -> string {
unreachable() unreachable()
} }
set_current_directory :: proc(path: string) -> (err: Errno) { set_current_directory :: proc(path: string) -> (err: Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := unix.sys_chdir(cstr) res := unix.sys_chdir(cstr)
if res < 0 { if res < 0 {
return _get_errno(res) return _get_errno(res)
} }
return ERROR_NONE return nil
} }
exit :: proc "contextless" (code: int) -> ! { exit :: proc "contextless" (code: int) -> ! {
@@ -979,16 +1006,19 @@ exit :: proc "contextless" (code: int) -> ! {
_unix_exit(c.int(code)) _unix_exit(c.int(code))
} }
@(require_results)
current_thread_id :: proc "contextless" () -> int { current_thread_id :: proc "contextless" () -> int {
return unix.sys_gettid() return unix.sys_gettid()
} }
@(require_results)
dlopen :: proc(filename: string, flags: int) -> rawptr { dlopen :: proc(filename: string, flags: int) -> rawptr {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(filename, context.temp_allocator) cstr := strings.clone_to_cstring(filename, context.temp_allocator)
handle := _unix_dlopen(cstr, c.int(flags)) handle := _unix_dlopen(cstr, c.int(flags))
return handle return handle
} }
@(require_results)
dlsym :: proc(handle: rawptr, symbol: string) -> rawptr { dlsym :: proc(handle: rawptr, symbol: string) -> rawptr {
assert(handle != nil) assert(handle != nil)
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
@@ -1004,6 +1034,7 @@ dlerror :: proc() -> string {
return string(_unix_dlerror()) return string(_unix_dlerror())
} }
@(require_results)
get_page_size :: proc() -> int { get_page_size :: proc() -> int {
// NOTE(tetra): The page size never changes, so why do anything complicated // NOTE(tetra): The page size never changes, so why do anything complicated
// if we don't have to. // if we don't have to.
@@ -1016,11 +1047,12 @@ get_page_size :: proc() -> int {
return page_size return page_size
} }
@(private) @(private, require_results)
_processor_core_count :: proc() -> int { _processor_core_count :: proc() -> int {
return int(_unix_get_nprocs()) return int(_unix_get_nprocs())
} }
@(require_results)
_alloc_command_line_arguments :: proc() -> []string { _alloc_command_line_arguments :: proc() -> []string {
res := make([]string, len(runtime.args__)) res := make([]string, len(runtime.args__))
for arg, i in runtime.args__ { for arg, i in runtime.args__ {
@@ -1029,117 +1061,120 @@ _alloc_command_line_arguments :: proc() -> []string {
return res return res
} }
socket :: proc(domain: int, type: int, protocol: int) -> (Socket, Errno) { @(require_results)
socket :: proc(domain: int, type: int, protocol: int) -> (Socket, Error) {
result := unix.sys_socket(domain, type, protocol) result := unix.sys_socket(domain, type, protocol)
if result < 0 { if result < 0 {
return 0, _get_errno(result) return 0, _get_errno(result)
} }
return Socket(result), ERROR_NONE return Socket(result), nil
} }
bind :: proc(sd: Socket, addr: ^SOCKADDR, len: socklen_t) -> (Errno) { bind :: proc(sd: Socket, addr: ^SOCKADDR, len: socklen_t) -> Error {
result := unix.sys_bind(int(sd), addr, len) result := unix.sys_bind(int(sd), addr, len)
if result < 0 { if result < 0 {
return _get_errno(result) return _get_errno(result)
} }
return ERROR_NONE return nil
} }
connect :: proc(sd: Socket, addr: ^SOCKADDR, len: socklen_t) -> (Errno) { connect :: proc(sd: Socket, addr: ^SOCKADDR, len: socklen_t) -> Error {
result := unix.sys_connect(int(sd), addr, len) result := unix.sys_connect(int(sd), addr, len)
if result < 0 { if result < 0 {
return _get_errno(result) return _get_errno(result)
} }
return ERROR_NONE return nil
} }
accept :: proc(sd: Socket, addr: ^SOCKADDR, len: rawptr) -> (Socket, Errno) { accept :: proc(sd: Socket, addr: ^SOCKADDR, len: rawptr) -> (Socket, Error) {
result := unix.sys_accept(int(sd), rawptr(addr), len) result := unix.sys_accept(int(sd), rawptr(addr), len)
if result < 0 { if result < 0 {
return 0, _get_errno(result) return 0, _get_errno(result)
} }
return Socket(result), ERROR_NONE return Socket(result), nil
} }
listen :: proc(sd: Socket, backlog: int) -> (Errno) { listen :: proc(sd: Socket, backlog: int) -> Error {
result := unix.sys_listen(int(sd), backlog) result := unix.sys_listen(int(sd), backlog)
if result < 0 { if result < 0 {
return _get_errno(result) return _get_errno(result)
} }
return ERROR_NONE return nil
} }
setsockopt :: proc(sd: Socket, level: int, optname: int, optval: rawptr, optlen: socklen_t) -> (Errno) { setsockopt :: proc(sd: Socket, level: int, optname: int, optval: rawptr, optlen: socklen_t) -> Error {
result := unix.sys_setsockopt(int(sd), level, optname, optval, optlen) result := unix.sys_setsockopt(int(sd), level, optname, optval, optlen)
if result < 0 { if result < 0 {
return _get_errno(result) return _get_errno(result)
} }
return ERROR_NONE return nil
} }
recvfrom :: proc(sd: Socket, data: []byte, flags: int, addr: ^SOCKADDR, addr_size: ^socklen_t) -> (u32, Errno) { recvfrom :: proc(sd: Socket, data: []byte, flags: int, addr: ^SOCKADDR, addr_size: ^socklen_t) -> (u32, Error) {
result := unix.sys_recvfrom(int(sd), raw_data(data), len(data), flags, addr, uintptr(addr_size)) result := unix.sys_recvfrom(int(sd), raw_data(data), len(data), flags, addr, uintptr(addr_size))
if result < 0 { if result < 0 {
return 0, _get_errno(int(result)) return 0, _get_errno(int(result))
} }
return u32(result), ERROR_NONE return u32(result), nil
} }
recv :: proc(sd: Socket, data: []byte, flags: int) -> (u32, Errno) { recv :: proc(sd: Socket, data: []byte, flags: int) -> (u32, Error) {
result := unix.sys_recvfrom(int(sd), raw_data(data), len(data), flags, nil, 0) result := unix.sys_recvfrom(int(sd), raw_data(data), len(data), flags, nil, 0)
if result < 0 { if result < 0 {
return 0, _get_errno(int(result)) return 0, _get_errno(int(result))
} }
return u32(result), ERROR_NONE return u32(result), nil
} }
sendto :: proc(sd: Socket, data: []u8, flags: int, addr: ^SOCKADDR, addrlen: socklen_t) -> (u32, Errno) { sendto :: proc(sd: Socket, data: []u8, flags: int, addr: ^SOCKADDR, addrlen: socklen_t) -> (u32, Error) {
result := unix.sys_sendto(int(sd), raw_data(data), len(data), flags, addr, addrlen) result := unix.sys_sendto(int(sd), raw_data(data), len(data), flags, addr, addrlen)
if result < 0 { if result < 0 {
return 0, _get_errno(int(result)) return 0, _get_errno(int(result))
} }
return u32(result), ERROR_NONE return u32(result), nil
} }
send :: proc(sd: Socket, data: []byte, flags: int) -> (u32, Errno) { send :: proc(sd: Socket, data: []byte, flags: int) -> (u32, Error) {
result := unix.sys_sendto(int(sd), raw_data(data), len(data), 0, nil, 0) result := unix.sys_sendto(int(sd), raw_data(data), len(data), 0, nil, 0)
if result < 0 { if result < 0 {
return 0, _get_errno(int(result)) return 0, _get_errno(int(result))
} }
return u32(result), ERROR_NONE return u32(result), nil
} }
shutdown :: proc(sd: Socket, how: int) -> (Errno) { shutdown :: proc(sd: Socket, how: int) -> Error {
result := unix.sys_shutdown(int(sd), how) result := unix.sys_shutdown(int(sd), how)
if result < 0 { if result < 0 {
return _get_errno(result) return _get_errno(result)
} }
return ERROR_NONE return nil
} }
fcntl :: proc(fd: int, cmd: int, arg: int) -> (int, Errno) { fcntl :: proc(fd: int, cmd: int, arg: int) -> (int, Error) {
result := unix.sys_fcntl(fd, cmd, arg) result := unix.sys_fcntl(fd, cmd, arg)
if result < 0 { if result < 0 {
return 0, _get_errno(result) return 0, _get_errno(result)
} }
return result, ERROR_NONE return result, nil
} }
poll :: proc(fds: []pollfd, timeout: int) -> (int, Errno) { @(require_results)
poll :: proc(fds: []pollfd, timeout: int) -> (int, Error) {
result := unix.sys_poll(raw_data(fds), uint(len(fds)), timeout) result := unix.sys_poll(raw_data(fds), uint(len(fds)), timeout)
if result < 0 { if result < 0 {
return 0, _get_errno(result) return 0, _get_errno(result)
} }
return result, ERROR_NONE return result, nil
} }
ppoll :: proc(fds: []pollfd, timeout: ^unix.timespec, sigmask: ^sigset_t) -> (int, Errno) { @(require_results)
ppoll :: proc(fds: []pollfd, timeout: ^unix.timespec, sigmask: ^sigset_t) -> (int, Error) {
result := unix.sys_ppoll(raw_data(fds), uint(len(fds)), timeout, sigmask, size_of(sigset_t)) result := unix.sys_ppoll(raw_data(fds), uint(len(fds)), timeout, sigmask, size_of(sigset_t))
if result < 0 { if result < 0 {
return 0, _get_errno(result) return 0, _get_errno(result)
} }
return result, ERROR_NONE return result, nil
} }
+391 -216
View File
@@ -9,150 +9,289 @@ import "core:c"
Handle :: distinct i32 Handle :: distinct i32
File_Time :: distinct u64 File_Time :: distinct u64
Errno :: distinct i32
INVALID_HANDLE :: ~Handle(0) INVALID_HANDLE :: ~Handle(0)
ERROR_NONE: Errno : 0 /* No error */ _Platform_Error :: enum i32 {
EPERM: Errno : 1 /* Operation not permitted */ NONE = 0,
ENOENT: Errno : 2 /* No such file or directory */ EPERM = 1, /* Operation not permitted */
EINTR: Errno : 4 /* Interrupted system call */ ENOENT = 2, /* No such file or directory */
ESRCH: Errno : 3 /* No such process */ EINTR = 4, /* Interrupted system call */
EIO: Errno : 5 /* Input/output error */ ESRCH = 3, /* No such process */
ENXIO: Errno : 6 /* Device not configured */ EIO = 5, /* Input/output error */
E2BIG: Errno : 7 /* Argument list too long */ ENXIO = 6, /* Device not configured */
ENOEXEC: Errno : 8 /* Exec format error */ E2BIG = 7, /* Argument list too long */
EBADF: Errno : 9 /* Bad file descriptor */ ENOEXEC = 8, /* Exec format error */
ECHILD: Errno : 10 /* No child processes */ EBADF = 9, /* Bad file descriptor */
EDEADLK: Errno : 11 /* Resource deadlock avoided. 11 was EAGAIN */ ECHILD = 10, /* No child processes */
ENOMEM: Errno : 12 /* Cannot allocate memory */ EDEADLK = 11, /* Resource deadlock avoided. 11 was EAGAIN */
EACCES: Errno : 13 /* Permission denied */ ENOMEM = 12, /* Cannot allocate memory */
EFAULT: Errno : 14 /* Bad address */ EACCES = 13, /* Permission denied */
ENOTBLK: Errno : 15 /* Block device required */ EFAULT = 14, /* Bad address */
EBUSY: Errno : 16 /* Device busy */ ENOTBLK = 15, /* Block device required */
EEXIST: Errno : 17 /* File exists */ EBUSY = 16, /* Device busy */
EXDEV: Errno : 18 /* Cross-device link */ EEXIST = 17, /* File exists */
ENODEV: Errno : 19 /* Operation not supported by device */ EXDEV = 18, /* Cross-device link */
ENOTDIR: Errno : 20 /* Not a directory */ ENODEV = 19, /* Operation not supported by device */
EISDIR: Errno : 21 /* Is a directory */ ENOTDIR = 20, /* Not a directory */
EINVAL: Errno : 22 /* Invalid argument */ EISDIR = 21, /* Is a directory */
ENFILE: Errno : 23 /* Too many open files in system */ EINVAL = 22, /* Invalid argument */
EMFILE: Errno : 24 /* Too many open files */ ENFILE = 23, /* Too many open files in system */
ENOTTY: Errno : 25 /* Inappropriate ioctl for device */ EMFILE = 24, /* Too many open files */
ETXTBSY: Errno : 26 /* Text file busy */ ENOTTY = 25, /* Inappropriate ioctl for device */
EFBIG: Errno : 27 /* File too large */ ETXTBSY = 26, /* Text file busy */
ENOSPC: Errno : 28 /* No space left on device */ EFBIG = 27, /* File too large */
ESPIPE: Errno : 29 /* Illegal seek */ ENOSPC = 28, /* No space left on device */
EROFS: Errno : 30 /* Read-only file system */ ESPIPE = 29, /* Illegal seek */
EMLINK: Errno : 31 /* Too many links */ EROFS = 30, /* Read-only file system */
EPIPE: Errno : 32 /* Broken pipe */ EMLINK = 31, /* Too many links */
EPIPE = 32, /* Broken pipe */
/* math software */
EDOM = 33, /* Numerical argument out of domain */
ERANGE = 34, /* Result too large or too small */
/* non-blocking and interrupt i/o */
EAGAIN = 35, /* Resource temporarily unavailable */
EWOULDBLOCK = EAGAIN, /* Operation would block */
EINPROGRESS = 36, /* Operation now in progress */
EALREADY = 37, /* Operation already in progress */
/* ipc/network software -- argument errors */
ENOTSOCK = 38, /* Socket operation on non-socket */
EDESTADDRREQ = 39, /* Destination address required */
EMSGSIZE = 40, /* Message too long */
EPROTOTYPE = 41, /* Protocol wrong type for socket */
ENOPROTOOPT = 42, /* Protocol option not available */
EPROTONOSUPPORT = 43, /* Protocol not supported */
ESOCKTNOSUPPORT = 44, /* Socket type not supported */
EOPNOTSUPP = 45, /* Operation not supported */
EPFNOSUPPORT = 46, /* Protocol family not supported */
EAFNOSUPPORT = 47, /* Address family not supported by protocol family */
EADDRINUSE = 48, /* Address already in use */
EADDRNOTAVAIL = 49, /* Can't assign requested address */
/* ipc/network software -- operational errors */
ENETDOWN = 50, /* Network is down */
ENETUNREACH = 51, /* Network is unreachable */
ENETRESET = 52, /* Network dropped connection on reset */
ECONNABORTED = 53, /* Software caused connection abort */
ECONNRESET = 54, /* Connection reset by peer */
ENOBUFS = 55, /* No buffer space available */
EISCONN = 56, /* Socket is already connected */
ENOTCONN = 57, /* Socket is not connected */
ESHUTDOWN = 58, /* Can't send after socket shutdown */
ETOOMANYREFS = 59, /* Too many references: can't splice */
ETIMEDOUT = 60, /* Operation timed out */
ECONNREFUSED = 61, /* Connection refused */
ELOOP = 62, /* Too many levels of symbolic links */
ENAMETOOLONG = 63, /* File name too long */
/* should be rearranged */
EHOSTDOWN = 64, /* Host is down */
EHOSTUNREACH = 65, /* No route to host */
ENOTEMPTY = 66, /* Directory not empty */
/* quotas & mush */
EPROCLIM = 67, /* Too many processes */
EUSERS = 68, /* Too many users */
EDQUOT = 69, /* Disc quota exceeded */
/* Network File System */
ESTALE = 70, /* Stale NFS file handle */
EREMOTE = 71, /* Too many levels of remote in path */
EBADRPC = 72, /* RPC struct is bad */
ERPCMISMATCH = 73, /* RPC version wrong */
EPROGUNAVAIL = 74, /* RPC prog. not avail */
EPROGMISMATCH = 75, /* Program version wrong */
EPROCUNAVAIL = 76, /* Bad procedure for program */
ENOLCK = 77, /* No locks available */
ENOSYS = 78, /* Function not implemented */
EFTYPE = 79, /* Inappropriate file type or format */
EAUTH = 80, /* Authentication error */
ENEEDAUTH = 81, /* Need authenticator */
/* SystemV IPC */
EIDRM = 82, /* Identifier removed */
ENOMSG = 83, /* No message of desired type */
EOVERFLOW = 84, /* Value too large to be stored in data type */
/* Wide/multibyte-character handling, ISO/IEC 9899/AMD1:1995 */
EILSEQ = 85, /* Illegal byte sequence */
/* From IEEE Std 1003.1-2001 */
/* Base, Realtime, Threads or Thread Priority Scheduling option errors */
ENOTSUP = 86, /* Not supported */
/* Realtime option errors */
ECANCELED = 87, /* Operation canceled */
/* Realtime, XSI STREAMS option errors */
EBADMSG = 88, /* Bad or Corrupt message */
/* XSI STREAMS option errors */
ENODATA = 89, /* No message available */
ENOSR = 90, /* No STREAM resources */
ENOSTR = 91, /* Not a STREAM */
ETIME = 92, /* STREAM ioctl timeout */
/* File system extended attribute errors */
ENOATTR = 93, /* Attribute not found */
/* Realtime, XSI STREAMS option errors */
EMULTIHOP = 94, /* Multihop attempted */
ENOLINK = 95, /* Link has been severed */
EPROTO = 96, /* Protocol error */
/* Robust mutexes */
EOWNERDEAD = 97, /* Previous owner died */
ENOTRECOVERABLE = 98, /* State not recoverable */
ELAST = 98, /* Must equal largest Error */
}
EPERM :: Platform_Error.EPERM /* Operation not permitted */
ENOENT :: Platform_Error.ENOENT /* No such file or directory */
EINTR :: Platform_Error.EINTR /* Interrupted system call */
ESRCH :: Platform_Error.ESRCH /* No such process */
EIO :: Platform_Error.EIO /* Input/output error */
ENXIO :: Platform_Error.ENXIO /* Device not configured */
E2BIG :: Platform_Error.E2BIG /* Argument list too long */
ENOEXEC :: Platform_Error.ENOEXEC /* Exec format error */
EBADF :: Platform_Error.EBADF /* Bad file descriptor */
ECHILD :: Platform_Error.ECHILD /* No child processes */
EDEADLK :: Platform_Error.EDEADLK /* Resource deadlock avoided. 11 was EAGAIN */
ENOMEM :: Platform_Error.ENOMEM /* Cannot allocate memory */
EACCES :: Platform_Error.EACCES /* Permission denied */
EFAULT :: Platform_Error.EFAULT /* Bad address */
ENOTBLK :: Platform_Error.ENOTBLK /* Block device required */
EBUSY :: Platform_Error.EBUSY /* Device busy */
EEXIST :: Platform_Error.EEXIST /* File exists */
EXDEV :: Platform_Error.EXDEV /* Cross-device link */
ENODEV :: Platform_Error.ENODEV /* Operation not supported by device */
ENOTDIR :: Platform_Error.ENOTDIR /* Not a directory */
EISDIR :: Platform_Error.EISDIR /* Is a directory */
EINVAL :: Platform_Error.EINVAL /* Invalid argument */
ENFILE :: Platform_Error.ENFILE /* Too many open files in system */
EMFILE :: Platform_Error.EMFILE /* Too many open files */
ENOTTY :: Platform_Error.ENOTTY /* Inappropriate ioctl for device */
ETXTBSY :: Platform_Error.ETXTBSY /* Text file busy */
EFBIG :: Platform_Error.EFBIG /* File too large */
ENOSPC :: Platform_Error.ENOSPC /* No space left on device */
ESPIPE :: Platform_Error.ESPIPE /* Illegal seek */
EROFS :: Platform_Error.EROFS /* Read-only file system */
EMLINK :: Platform_Error.EMLINK /* Too many links */
EPIPE :: Platform_Error.EPIPE /* Broken pipe */
/* math software */ /* math software */
EDOM: Errno : 33 /* Numerical argument out of domain */ EDOM :: Platform_Error.EDOM /* Numerical argument out of domain */
ERANGE: Errno : 34 /* Result too large or too small */ ERANGE :: Platform_Error.ERANGE /* Result too large or too small */
/* non-blocking and interrupt i/o */ /* non-blocking and interrupt i/o */
EAGAIN: Errno : 35 /* Resource temporarily unavailable */ EAGAIN :: Platform_Error.EAGAIN /* Resource temporarily unavailable */
EWOULDBLOCK: Errno : EAGAIN /* Operation would block */ EWOULDBLOCK :: EAGAIN /* Operation would block */
EINPROGRESS: Errno : 36 /* Operation now in progress */ EINPROGRESS :: Platform_Error.EINPROGRESS /* Operation now in progress */
EALREADY: Errno : 37 /* Operation already in progress */ EALREADY :: Platform_Error.EALREADY /* Operation already in progress */
/* ipc/network software -- argument errors */ /* ipc/network software -- argument errors */
ENOTSOCK: Errno : 38 /* Socket operation on non-socket */ ENOTSOCK :: Platform_Error.ENOTSOCK /* Socket operation on non-socket */
EDESTADDRREQ: Errno : 39 /* Destination address required */ EDESTADDRREQ :: Platform_Error.EDESTADDRREQ /* Destination address required */
EMSGSIZE: Errno : 40 /* Message too long */ EMSGSIZE :: Platform_Error.EMSGSIZE /* Message too long */
EPROTOTYPE: Errno : 41 /* Protocol wrong type for socket */ EPROTOTYPE :: Platform_Error.EPROTOTYPE /* Protocol wrong type for socket */
ENOPROTOOPT: Errno : 42 /* Protocol option not available */ ENOPROTOOPT :: Platform_Error.ENOPROTOOPT /* Protocol option not available */
EPROTONOSUPPORT: Errno : 43 /* Protocol not supported */ EPROTONOSUPPORT :: Platform_Error.EPROTONOSUPPORT /* Protocol not supported */
ESOCKTNOSUPPORT: Errno : 44 /* Socket type not supported */ ESOCKTNOSUPPORT :: Platform_Error.ESOCKTNOSUPPORT /* Socket type not supported */
EOPNOTSUPP: Errno : 45 /* Operation not supported */ EOPNOTSUPP :: Platform_Error.EOPNOTSUPP /* Operation not supported */
EPFNOSUPPORT: Errno : 46 /* Protocol family not supported */ EPFNOSUPPORT :: Platform_Error.EPFNOSUPPORT /* Protocol family not supported */
EAFNOSUPPORT: Errno : 47 /* Address family not supported by protocol family */ EAFNOSUPPORT :: Platform_Error.EAFNOSUPPORT /* Address family not supported by protocol family */
EADDRINUSE: Errno : 48 /* Address already in use */ EADDRINUSE :: Platform_Error.EADDRINUSE /* Address already in use */
EADDRNOTAVAIL: Errno : 49 /* Can't assign requested address */ EADDRNOTAVAIL :: Platform_Error.EADDRNOTAVAIL /* Can't assign requested address */
/* ipc/network software -- operational errors */ /* ipc/network software -- operational errors */
ENETDOWN: Errno : 50 /* Network is down */ ENETDOWN :: Platform_Error.ENETDOWN /* Network is down */
ENETUNREACH: Errno : 51 /* Network is unreachable */ ENETUNREACH :: Platform_Error.ENETUNREACH /* Network is unreachable */
ENETRESET: Errno : 52 /* Network dropped connection on reset */ ENETRESET :: Platform_Error.ENETRESET /* Network dropped connection on reset */
ECONNABORTED: Errno : 53 /* Software caused connection abort */ ECONNABORTED :: Platform_Error.ECONNABORTED /* Software caused connection abort */
ECONNRESET: Errno : 54 /* Connection reset by peer */ ECONNRESET :: Platform_Error.ECONNRESET /* Connection reset by peer */
ENOBUFS: Errno : 55 /* No buffer space available */ ENOBUFS :: Platform_Error.ENOBUFS /* No buffer space available */
EISCONN: Errno : 56 /* Socket is already connected */ EISCONN :: Platform_Error.EISCONN /* Socket is already connected */
ENOTCONN: Errno : 57 /* Socket is not connected */ ENOTCONN :: Platform_Error.ENOTCONN /* Socket is not connected */
ESHUTDOWN: Errno : 58 /* Can't send after socket shutdown */ ESHUTDOWN :: Platform_Error.ESHUTDOWN /* Can't send after socket shutdown */
ETOOMANYREFS: Errno : 59 /* Too many references: can't splice */ ETOOMANYREFS :: Platform_Error.ETOOMANYREFS /* Too many references: can't splice */
ETIMEDOUT: Errno : 60 /* Operation timed out */ ETIMEDOUT :: Platform_Error.ETIMEDOUT /* Operation timed out */
ECONNREFUSED: Errno : 61 /* Connection refused */ ECONNREFUSED :: Platform_Error.ECONNREFUSED /* Connection refused */
ELOOP: Errno : 62 /* Too many levels of symbolic links */ ELOOP :: Platform_Error.ELOOP /* Too many levels of symbolic links */
ENAMETOOLONG: Errno : 63 /* File name too long */ ENAMETOOLONG :: Platform_Error.ENAMETOOLONG /* File name too long */
/* should be rearranged */ /* should be rearranged */
EHOSTDOWN: Errno : 64 /* Host is down */ EHOSTDOWN :: Platform_Error.EHOSTDOWN /* Host is down */
EHOSTUNREACH: Errno : 65 /* No route to host */ EHOSTUNREACH :: Platform_Error.EHOSTUNREACH /* No route to host */
ENOTEMPTY: Errno : 66 /* Directory not empty */ ENOTEMPTY :: Platform_Error.ENOTEMPTY /* Directory not empty */
/* quotas & mush */ /* quotas & mush */
EPROCLIM: Errno : 67 /* Too many processes */ EPROCLIM :: Platform_Error.EPROCLIM /* Too many processes */
EUSERS: Errno : 68 /* Too many users */ EUSERS :: Platform_Error.EUSERS /* Too many users */
EDQUOT: Errno : 69 /* Disc quota exceeded */ EDQUOT :: Platform_Error.EDQUOT /* Disc quota exceeded */
/* Network File System */ /* Network File System */
ESTALE: Errno : 70 /* Stale NFS file handle */ ESTALE :: Platform_Error.ESTALE /* Stale NFS file handle */
EREMOTE: Errno : 71 /* Too many levels of remote in path */ EREMOTE :: Platform_Error.EREMOTE /* Too many levels of remote in path */
EBADRPC: Errno : 72 /* RPC struct is bad */ EBADRPC :: Platform_Error.EBADRPC /* RPC struct is bad */
ERPCMISMATCH: Errno : 73 /* RPC version wrong */ ERPCMISMATCH :: Platform_Error.ERPCMISMATCH /* RPC version wrong */
EPROGUNAVAIL: Errno : 74 /* RPC prog. not avail */ EPROGUNAVAIL :: Platform_Error.EPROGUNAVAIL /* RPC prog. not avail */
EPROGMISMATCH: Errno : 75 /* Program version wrong */ EPROGMISMATCH :: Platform_Error.EPROGMISMATCH /* Program version wrong */
EPROCUNAVAIL: Errno : 76 /* Bad procedure for program */ EPROCUNAVAIL :: Platform_Error.EPROCUNAVAIL /* Bad procedure for program */
ENOLCK: Errno : 77 /* No locks available */ ENOLCK :: Platform_Error.ENOLCK /* No locks available */
ENOSYS: Errno : 78 /* Function not implemented */ ENOSYS :: Platform_Error.ENOSYS /* Function not implemented */
EFTYPE: Errno : 79 /* Inappropriate file type or format */ EFTYPE :: Platform_Error.EFTYPE /* Inappropriate file type or format */
EAUTH: Errno : 80 /* Authentication error */ EAUTH :: Platform_Error.EAUTH /* Authentication error */
ENEEDAUTH: Errno : 81 /* Need authenticator */ ENEEDAUTH :: Platform_Error.ENEEDAUTH /* Need authenticator */
/* SystemV IPC */ /* SystemV IPC */
EIDRM: Errno : 82 /* Identifier removed */ EIDRM :: Platform_Error.EIDRM /* Identifier removed */
ENOMSG: Errno : 83 /* No message of desired type */ ENOMSG :: Platform_Error.ENOMSG /* No message of desired type */
EOVERFLOW: Errno : 84 /* Value too large to be stored in data type */ EOVERFLOW :: Platform_Error.EOVERFLOW /* Value too large to be stored in data type */
/* Wide/multibyte-character handling, ISO/IEC 9899/AMD1:1995 */ /* Wide/multibyte-character handling, ISO/IEC 9899/AMD1:1995 */
EILSEQ: Errno : 85 /* Illegal byte sequence */ EILSEQ :: Platform_Error.EILSEQ /* Illegal byte sequence */
/* From IEEE Std 1003.1-2001 */ /* From IEEE Std 1003.1-2001 */
/* Base, Realtime, Threads or Thread Priority Scheduling option errors */ /* Base, Realtime, Threads or Thread Priority Scheduling option errors */
ENOTSUP: Errno : 86 /* Not supported */ ENOTSUP :: Platform_Error.ENOTSUP /* Not supported */
/* Realtime option errors */ /* Realtime option errors */
ECANCELED: Errno : 87 /* Operation canceled */ ECANCELED :: Platform_Error.ECANCELED /* Operation canceled */
/* Realtime, XSI STREAMS option errors */ /* Realtime, XSI STREAMS option errors */
EBADMSG: Errno : 88 /* Bad or Corrupt message */ EBADMSG :: Platform_Error.EBADMSG /* Bad or Corrupt message */
/* XSI STREAMS option errors */ /* XSI STREAMS option errors */
ENODATA: Errno : 89 /* No message available */ ENODATA :: Platform_Error.ENODATA /* No message available */
ENOSR: Errno : 90 /* No STREAM resources */ ENOSR :: Platform_Error.ENOSR /* No STREAM resources */
ENOSTR: Errno : 91 /* Not a STREAM */ ENOSTR :: Platform_Error.ENOSTR /* Not a STREAM */
ETIME: Errno : 92 /* STREAM ioctl timeout */ ETIME :: Platform_Error.ETIME /* STREAM ioctl timeout */
/* File system extended attribute errors */ /* File system extended attribute errors */
ENOATTR: Errno : 93 /* Attribute not found */ ENOATTR :: Platform_Error.ENOATTR /* Attribute not found */
/* Realtime, XSI STREAMS option errors */ /* Realtime, XSI STREAMS option errors */
EMULTIHOP: Errno : 94 /* Multihop attempted */ EMULTIHOP :: Platform_Error.EMULTIHOP /* Multihop attempted */
ENOLINK: Errno : 95 /* Link has been severed */ ENOLINK :: Platform_Error.ENOLINK /* Link has been severed */
EPROTO: Errno : 96 /* Protocol error */ EPROTO :: Platform_Error.EPROTO /* Protocol error */
/* Robust mutexes */ /* Robust mutexes */
EOWNERDEAD: Errno : 97 /* Previous owner died */ EOWNERDEAD :: Platform_Error.EOWNERDEAD /* Previous owner died */
ENOTRECOVERABLE: Errno : 98 /* State not recoverable */ ENOTRECOVERABLE :: Platform_Error.ENOTRECOVERABLE /* State not recoverable */
ELAST: Errno : 98 /* Must equal largest errno */ ELAST :: Platform_Error.ELAST /* Must equal largest Error */
/* end of errno */ /* end of Error */
O_RDONLY :: 0x000000000 O_RDONLY :: 0x000000000
O_WRONLY :: 0x000000001 O_WRONLY :: 0x000000001
@@ -268,13 +407,13 @@ S_ISUID :: 0o4000 // Set user id on execution
S_ISGID :: 0o2000 // Set group id on execution S_ISGID :: 0o2000 // Set group id on execution
S_ISVTX :: 0o1000 // Directory restrcted delete S_ISVTX :: 0o1000 // Directory restrcted delete
S_ISLNK :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFLNK } @(require_results) S_ISLNK :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFLNK }
S_ISREG :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFREG } @(require_results) S_ISREG :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFREG }
S_ISDIR :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFDIR } @(require_results) S_ISDIR :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFDIR }
S_ISCHR :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFCHR } @(require_results) S_ISCHR :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFCHR }
S_ISBLK :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFBLK } @(require_results) S_ISBLK :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFBLK }
S_ISFIFO :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFIFO } @(require_results) S_ISFIFO :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFIFO }
S_ISSOCK :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFSOCK } @(require_results) S_ISSOCK :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFSOCK }
F_OK :: 0 // Test for file existance F_OK :: 0 // Test for file existance
X_OK :: 1 // Test for execute permission X_OK :: 1 // Test for execute permission
@@ -334,154 +473,186 @@ foreign libc {
// NOTE(phix): Perhaps share the following functions with FreeBSD if they turn out to be the same in the end. // NOTE(phix): Perhaps share the following functions with FreeBSD if they turn out to be the same in the end.
@(require_results)
is_path_separator :: proc(r: rune) -> bool { is_path_separator :: proc(r: rune) -> bool {
return r == '/' return r == '/'
} }
get_last_error :: proc "contextless" () -> int { @(require_results, no_instrumentation)
return int(__errno_location()^) get_last_error :: proc "contextless" () -> Error {
return Platform_Error(__errno_location()^)
} }
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) { @(require_results)
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
handle := _unix_open(cstr, c.int(flags), c.int(mode)) handle := _unix_open(cstr, c.int(flags), c.int(mode))
if handle == -1 { if handle == -1 {
return INVALID_HANDLE, Errno(get_last_error()) return INVALID_HANDLE, get_last_error()
} }
return handle, ERROR_NONE return handle, nil
} }
close :: proc(fd: Handle) -> Errno { close :: proc(fd: Handle) -> Error {
result := _unix_close(fd) result := _unix_close(fd)
if result == -1 { if result == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
}
flush :: proc(fd: Handle) -> Error {
// do nothing
return nil
} }
// We set a max of 1GB to keep alignment and to be safe. // We set a max of 1GB to keep alignment and to be safe.
@(private) @(private)
MAX_RW :: 1 << 30 MAX_RW :: 1 << 30
read :: proc(fd: Handle, data: []byte) -> (int, Errno) { read :: proc(fd: Handle, data: []byte) -> (int, Error) {
to_read := min(c.size_t(len(data)), MAX_RW) to_read := min(c.size_t(len(data)), MAX_RW)
bytes_read := _unix_read(fd, &data[0], to_read) bytes_read := _unix_read(fd, &data[0], to_read)
if bytes_read == -1 { if bytes_read == -1 {
return -1, Errno(get_last_error()) return -1, get_last_error()
} }
return int(bytes_read), ERROR_NONE return int(bytes_read), nil
} }
write :: proc(fd: Handle, data: []byte) -> (int, Errno) { write :: proc(fd: Handle, data: []byte) -> (int, Error) {
if len(data) == 0 { if len(data) == 0 {
return 0, ERROR_NONE return 0, nil
} }
to_write := min(c.size_t(len(data)), MAX_RW) to_write := min(c.size_t(len(data)), MAX_RW)
bytes_written := _unix_write(fd, &data[0], to_write) bytes_written := _unix_write(fd, &data[0], to_write)
if bytes_written == -1 { if bytes_written == -1 {
return -1, Errno(get_last_error()) return -1, get_last_error()
} }
return int(bytes_written), ERROR_NONE return int(bytes_written), nil
} }
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
curr := seek(fd, offset, SEEK_CUR) or_return
n, err = read(fd, data)
_, err1 := seek(fd, curr, SEEK_SET)
if err1 != nil && err == nil {
err = err1
}
return
}
write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
curr := seek(fd, offset, SEEK_CUR) or_return
n, err = write(fd, data)
_, err1 := seek(fd, curr, SEEK_SET)
if err1 != nil && err == nil {
err = err1
}
return
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
res := _unix_seek(fd, offset, c.int(whence)) res := _unix_seek(fd, offset, c.int(whence))
if res == -1 { if res == -1 {
return -1, Errno(get_last_error()) return -1, get_last_error()
} }
return res, ERROR_NONE return res, nil
} }
file_size :: proc(fd: Handle) -> (i64, Errno) { @(require_results)
s, err := _fstat(fd) file_size :: proc(fd: Handle) -> (size: i64, err: Error) {
if err != ERROR_NONE { size = -1
return -1, err s := _fstat(fd) or_return
} size = s.size
return s.size, ERROR_NONE return
} }
rename :: proc(old_path, new_path: string) -> Errno { rename :: proc(old_path, new_path: string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator) old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator)
new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator) new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator)
res := _unix_rename(old_path_cstr, new_path_cstr) res := _unix_rename(old_path_cstr, new_path_cstr)
if res == -1 { if res == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
remove :: proc(path: string) -> Errno { remove :: proc(path: string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator) path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_unlink(path_cstr) res := _unix_unlink(path_cstr)
if res == -1 { if res == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
make_directory :: proc(path: string, mode: mode_t = 0o775) -> Errno { make_directory :: proc(path: string, mode: mode_t = 0o775) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator) path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_mkdir(path_cstr, mode) res := _unix_mkdir(path_cstr, mode)
if res == -1 { if res == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
remove_directory :: proc(path: string) -> Errno { remove_directory :: proc(path: string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator) path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_rmdir(path_cstr) res := _unix_rmdir(path_cstr)
if res == -1 { if res == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
@(require_results)
is_file_handle :: proc(fd: Handle) -> bool { is_file_handle :: proc(fd: Handle) -> bool {
s, err := _fstat(fd) s, err := _fstat(fd)
if err != ERROR_NONE { if err != nil {
return false return false
} }
return S_ISREG(s.mode) return S_ISREG(s.mode)
} }
@(require_results)
is_file_path :: proc(path: string, follow_links: bool = true) -> bool { is_file_path :: proc(path: string, follow_links: bool = true) -> bool {
s: OS_Stat s: OS_Stat
err: Errno err: Error
if follow_links { if follow_links {
s, err = _stat(path) s, err = _stat(path)
} else { } else {
s, err = _lstat(path) s, err = _lstat(path)
} }
if err != ERROR_NONE { if err != nil {
return false return false
} }
return S_ISREG(s.mode) return S_ISREG(s.mode)
} }
@(require_results)
is_dir_handle :: proc(fd: Handle) -> bool { is_dir_handle :: proc(fd: Handle) -> bool {
s, err := _fstat(fd) s, err := _fstat(fd)
if err != ERROR_NONE { if err != nil {
return false return false
} }
return S_ISDIR(s.mode) return S_ISDIR(s.mode)
} }
@(require_results)
is_dir_path :: proc(path: string, follow_links: bool = true) -> bool { is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
s: OS_Stat s: OS_Stat
err: Errno err: Error
if follow_links { if follow_links {
s, err = _stat(path) s, err = _stat(path)
} else { } else {
s, err = _lstat(path) s, err = _lstat(path)
} }
if err != ERROR_NONE { if err != nil {
return false return false
} }
return S_ISDIR(s.mode) return S_ISDIR(s.mode)
@@ -490,6 +661,7 @@ is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
is_file :: proc {is_file_path, is_file_handle} is_file :: proc {is_file_path, is_file_handle}
is_dir :: proc {is_dir_path, is_dir_handle} is_dir :: proc {is_dir_path, is_dir_handle}
@(require_results)
exists :: proc(path: string) -> bool { exists :: proc(path: string) -> bool {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cpath := strings.clone_to_cstring(path, context.temp_allocator) cpath := strings.clone_to_cstring(path, context.temp_allocator)
@@ -497,12 +669,13 @@ exists :: proc(path: string) -> bool {
return res == 0 return res == 0
} }
fcntl :: proc(fd: int, cmd: int, arg: int) -> (int, Errno) { @(require_results)
fcntl :: proc(fd: int, cmd: int, arg: int) -> (int, Error) {
result := _unix_fcntl(Handle(fd), c.int(cmd), uintptr(arg)) result := _unix_fcntl(Handle(fd), c.int(cmd), uintptr(arg))
if result < 0 { if result < 0 {
return 0, Errno(get_last_error()) return 0, get_last_error()
} }
return int(result), ERROR_NONE return int(result), nil
} }
// NOTE(bill): Uses startup to initialize it // NOTE(bill): Uses startup to initialize it
@@ -511,38 +684,34 @@ stdin: Handle = 0
stdout: Handle = 1 stdout: Handle = 1
stderr: Handle = 2 stderr: Handle = 2
last_write_time :: proc(fd: Handle) -> (File_Time, Errno) { @(require_results)
s, err := _fstat(fd) last_write_time :: proc(fd: Handle) -> (time: File_Time, err: Error) {
if err != ERROR_NONE { s := _fstat(fd) or_return
return 0, err
}
modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
return File_Time(modified), ERROR_NONE return File_Time(modified), nil
} }
last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) { @(require_results)
s, err := _stat(name) last_write_time_by_name :: proc(name: string) -> (time: File_Time, err: Error) {
if err != ERROR_NONE { s := _stat(name) or_return
return 0, err
}
modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
return File_Time(modified), ERROR_NONE return File_Time(modified), nil
} }
@private @(private, require_results)
_stat :: proc(path: string) -> (OS_Stat, Errno) { _stat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
s: OS_Stat = --- s: OS_Stat = ---
result := _unix_lstat(cstr, &s) result := _unix_lstat(cstr, &s)
if result == -1 { if result == -1 {
return s, Errno(get_last_error()) return s, get_last_error()
} }
return s, ERROR_NONE return s, nil
} }
@private @(private, require_results)
_lstat :: proc(path: string) -> (OS_Stat, Errno) { _lstat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -550,54 +719,54 @@ _lstat :: proc(path: string) -> (OS_Stat, Errno) {
s: OS_Stat = --- s: OS_Stat = ---
res := _unix_lstat(cstr, &s) res := _unix_lstat(cstr, &s)
if res == -1 { if res == -1 {
return s, Errno(get_last_error()) return s, get_last_error()
} }
return s, ERROR_NONE return s, nil
} }
@private @(private, require_results)
_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) { _fstat :: proc(fd: Handle) -> (OS_Stat, Error) {
s: OS_Stat = --- s: OS_Stat = ---
result := _unix_fstat(fd, &s) result := _unix_fstat(fd, &s)
if result == -1 { if result == -1 {
return s, Errno(get_last_error()) return s, get_last_error()
} }
return s, ERROR_NONE return s, nil
} }
@private @(private, require_results)
_fdopendir :: proc(fd: Handle) -> (Dir, Errno) { _fdopendir :: proc(fd: Handle) -> (Dir, Error) {
dirp := _unix_fdopendir(fd) dirp := _unix_fdopendir(fd)
if dirp == cast(Dir)nil { if dirp == cast(Dir)nil {
return nil, Errno(get_last_error()) return nil, get_last_error()
} }
return dirp, ERROR_NONE return dirp, nil
} }
@private @(private)
_closedir :: proc(dirp: Dir) -> Errno { _closedir :: proc(dirp: Dir) -> Error {
rc := _unix_closedir(dirp) rc := _unix_closedir(dirp)
if rc != 0 { if rc != 0 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
@private @(private)
_rewinddir :: proc(dirp: Dir) { _rewinddir :: proc(dirp: Dir) {
_unix_rewinddir(dirp) _unix_rewinddir(dirp)
} }
@private @(private, require_results)
_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) { _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Error, end_of_stream: bool) {
result: ^Dirent result: ^Dirent
rc := _unix_readdir_r(dirp, &entry, &result) rc := _unix_readdir_r(dirp, &entry, &result)
if rc != 0 { if rc != 0 {
err = Errno(get_last_error()) err = get_last_error()
return return
} }
err = ERROR_NONE err = nil
if result == nil { if result == nil {
end_of_stream = true end_of_stream = true
@@ -607,8 +776,8 @@ _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool)
return return
} }
@private @(private, require_results)
_readlink :: proc(path: string) -> (string, Errno) { _readlink :: proc(path: string) -> (string, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator) runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
path_cstr := strings.clone_to_cstring(path, context.temp_allocator) path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -619,31 +788,28 @@ _readlink :: proc(path: string) -> (string, Errno) {
rc := _unix_readlink(path_cstr, &(buf[0]), bufsz) rc := _unix_readlink(path_cstr, &(buf[0]), bufsz)
if rc == -1 { if rc == -1 {
delete(buf) delete(buf)
return "", Errno(get_last_error()) return "", get_last_error()
} else if rc == int(bufsz) { } else if rc == int(bufsz) {
bufsz += MAX_PATH bufsz += MAX_PATH
delete(buf) delete(buf)
buf = make([]byte, bufsz) buf = make([]byte, bufsz)
} else { } else {
return strings.string_from_ptr(&buf[0], rc), ERROR_NONE return strings.string_from_ptr(&buf[0], rc), nil
} }
} }
return "", Errno{} return "", Error{}
} }
absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) { @(require_results)
absolute_path_from_handle :: proc(fd: Handle) -> (path: string, err: Error) {
buf: [MAX_PATH]byte buf: [MAX_PATH]byte
_, err := fcntl(int(fd), F_GETPATH, int(uintptr(&buf[0]))) _ = fcntl(int(fd), F_GETPATH, int(uintptr(&buf[0]))) or_return
if err != ERROR_NONE { return strings.clone_from_cstring(cstring(&buf[0]))
return "", err
}
path := strings.clone_from_cstring(cstring(&buf[0]))
return path, err
} }
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) { @(require_results)
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
rel := rel rel := rel
if rel == "" { if rel == "" {
rel = "." rel = "."
@@ -654,26 +820,27 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
path_ptr := _unix_realpath(rel_cstr, nil) path_ptr := _unix_realpath(rel_cstr, nil)
if path_ptr == nil { if path_ptr == nil {
return "", Errno(get_last_error()) return "", get_last_error()
} }
defer _unix_free(path_ptr) defer _unix_free(path_ptr)
path = strings.clone(string(cstring(path_ptr))) path = strings.clone(string(cstring(path_ptr)))
return path, ERROR_NONE return path, nil
} }
access :: proc(path: string, mask: int) -> (bool, Errno) { access :: proc(path: string, mask: int) -> (bool, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
result := _unix_access(cstr, c.int(mask)) result := _unix_access(cstr, c.int(mask))
if result == -1 { if result == -1 {
return false, Errno(get_last_error()) return false, get_last_error()
} }
return true, ERROR_NONE return true, nil
} }
@(require_results)
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
@@ -685,11 +852,13 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
return strings.clone(string(cstr), allocator), true return strings.clone(string(cstr), allocator), true
} }
@(require_results)
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) { get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
value, _ = lookup_env(key, allocator) value, _ = lookup_env(key, allocator)
return return
} }
@(require_results)
get_current_directory :: proc() -> string { get_current_directory :: proc() -> string {
// NOTE(tetra): I would use PATH_MAX here, but I was not able to find // NOTE(tetra): I would use PATH_MAX here, but I was not able to find
// an authoritative value for it across all systems. // an authoritative value for it across all systems.
@@ -701,7 +870,7 @@ get_current_directory :: proc() -> string {
if cwd != nil { if cwd != nil {
return string(cwd) return string(cwd)
} }
if Errno(get_last_error()) != ERANGE { if get_last_error() != ERANGE {
delete(buf) delete(buf)
return "" return ""
} }
@@ -710,14 +879,14 @@ get_current_directory :: proc() -> string {
unreachable() unreachable()
} }
set_current_directory :: proc(path: string) -> (err: Errno) { set_current_directory :: proc(path: string) -> (err: Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_chdir(cstr) res := _unix_chdir(cstr)
if res == -1 { if res == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
exit :: proc "contextless" (code: int) -> ! { exit :: proc "contextless" (code: int) -> ! {
@@ -725,10 +894,12 @@ exit :: proc "contextless" (code: int) -> ! {
_unix_exit(c.int(code)) _unix_exit(c.int(code))
} }
@(require_results)
current_thread_id :: proc "contextless" () -> int { current_thread_id :: proc "contextless" () -> int {
return int(_lwp_self()) return int(_lwp_self())
} }
@(require_results)
dlopen :: proc(filename: string, flags: int) -> rawptr { dlopen :: proc(filename: string, flags: int) -> rawptr {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(filename, context.temp_allocator) cstr := strings.clone_to_cstring(filename, context.temp_allocator)
@@ -736,6 +907,7 @@ dlopen :: proc(filename: string, flags: int) -> rawptr {
return handle return handle
} }
@(require_results)
dlsym :: proc(handle: rawptr, symbol: string) -> rawptr { dlsym :: proc(handle: rawptr, symbol: string) -> rawptr {
assert(handle != nil) assert(handle != nil)
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
@@ -749,10 +921,12 @@ dlclose :: proc(handle: rawptr) -> bool {
return _unix_dlclose(handle) == 0 return _unix_dlclose(handle) == 0
} }
@(require_results)
dlerror :: proc() -> string { dlerror :: proc() -> string {
return string(_unix_dlerror()) return string(_unix_dlerror())
} }
@(require_results)
get_page_size :: proc() -> int { get_page_size :: proc() -> int {
// NOTE(tetra): The page size never changes, so why do anything complicated // NOTE(tetra): The page size never changes, so why do anything complicated
// if we don't have to. // if we don't have to.
@@ -765,7 +939,7 @@ get_page_size :: proc() -> int {
return page_size return page_size
} }
@(private) @(private, require_results)
_processor_core_count :: proc() -> int { _processor_core_count :: proc() -> int {
count : int = 0 count : int = 0
count_size := size_of(count) count_size := size_of(count)
@@ -778,6 +952,7 @@ _processor_core_count :: proc() -> int {
return 1 return 1
} }
@(require_results)
_alloc_command_line_arguments :: proc() -> []string { _alloc_command_line_arguments :: proc() -> []string {
res := make([]string, len(runtime.args__)) res := make([]string, len(runtime.args__))
for arg, i in runtime.args__ { for arg, i in runtime.args__ {
+341 -204
View File
@@ -9,108 +9,205 @@ import "base:runtime"
Handle :: distinct i32 Handle :: distinct i32
Pid :: distinct i32 Pid :: distinct i32
File_Time :: distinct u64 File_Time :: distinct u64
Errno :: distinct i32
INVALID_HANDLE :: ~Handle(0) INVALID_HANDLE :: ~Handle(0)
ERROR_NONE: Errno: 0 _Platform_Error :: enum i32 {
NONE = 0,
EPERM = 1,
ENOENT = 2,
ESRCH = 3,
EINTR = 4,
EIO = 5,
ENXIO = 6,
E2BIG = 7,
ENOEXEC = 8,
EBADF = 9,
ECHILD = 10,
EDEADLK = 11,
ENOMEM = 12,
EACCES = 13,
EFAULT = 14,
ENOTBLK = 15,
EBUSY = 16,
EEXIST = 17,
EXDEV = 18,
ENODEV = 19,
ENOTDIR = 20,
EISDIR = 21,
EINVAL = 22,
ENFILE = 23,
EMFILE = 24,
ENOTTY = 25,
ETXTBSY = 26,
EFBIG = 27,
ENOSPC = 28,
ESPIPE = 29,
EROFS = 30,
EMLINK = 31,
EPIPE = 32,
EDOM = 33,
ERANGE = 34,
EAGAIN = 35,
EWOULDBLOCK = EAGAIN,
EINPROGRESS = 36,
EALREADY = 37,
ENOTSOCK = 38,
EDESTADDRREQ = 39,
EMSGSIZE = 40,
EPROTOTYPE = 41,
ENOPROTOOPT = 42,
EPROTONOSUPPORT = 43,
ESOCKTNOSUPPORT = 44,
EOPNOTSUPP = 45,
EPFNOSUPPORT = 46,
EAFNOSUPPORT = 47,
EADDRINUSE = 48,
EADDRNOTAVAIL = 49,
ENETDOWN = 50,
ENETUNREACH = 51,
ENETRESET = 52,
ECONNABORTED = 53,
ECONNRESET = 54,
ENOBUFS = 55,
EISCONN = 56,
ENOTCONN = 57,
ESHUTDOWN = 58,
ETOOMANYREFS = 59,
ETIMEDOUT = 60,
ECONNREFUSED = 61,
ELOOP = 62,
ENAMETOOLONG = 63,
EHOSTDOWN = 64,
EHOSTUNREACH = 65,
ENOTEMPTY = 66,
EPROCLIM = 67,
EUSERS = 68,
EDQUOT = 69,
ESTALE = 70,
EREMOTE = 71,
EBADRPC = 72,
ERPCMISMATCH = 73,
EPROGUNAVAIL = 74,
EPROGMISMATCH = 75,
EPROCUNAVAIL = 76,
ENOLCK = 77,
ENOSYS = 78,
EFTYPE = 79,
EAUTH = 80,
ENEEDAUTH = 81,
EIPSEC = 82,
ENOATTR = 83,
EILSEQ = 84,
ENOMEDIUM = 85,
EMEDIUMTYPE = 86,
EOVERFLOW = 87,
ECANCELED = 88,
EIDRM = 89,
ENOMSG = 90,
ENOTSUP = 91,
EBADMSG = 92,
ENOTRECOVERABLE = 93,
EOWNERDEAD = 94,
EPROTO = 95,
}
EPERM: Errno: 1 EPERM :: Platform_Error.EPERM
ENOENT: Errno: 2 ENOENT :: Platform_Error.ENOENT
ESRCH: Errno: 3 ESRCH :: Platform_Error.ESRCH
EINTR: Errno: 4 EINTR :: Platform_Error.EINTR
EIO: Errno: 5 EIO :: Platform_Error.EIO
ENXIO: Errno: 6 ENXIO :: Platform_Error.ENXIO
E2BIG: Errno: 7 E2BIG :: Platform_Error.E2BIG
ENOEXEC: Errno: 8 ENOEXEC :: Platform_Error.ENOEXEC
EBADF: Errno: 9 EBADF :: Platform_Error.EBADF
ECHILD: Errno: 10 ECHILD :: Platform_Error.ECHILD
EDEADLK: Errno: 11 EDEADLK :: Platform_Error.EDEADLK
ENOMEM: Errno: 12 ENOMEM :: Platform_Error.ENOMEM
EACCES: Errno: 13 EACCES :: Platform_Error.EACCES
EFAULT: Errno: 14 EFAULT :: Platform_Error.EFAULT
ENOTBLK: Errno: 15 ENOTBLK :: Platform_Error.ENOTBLK
EBUSY: Errno: 16 EBUSY :: Platform_Error.EBUSY
EEXIST: Errno: 17 EEXIST :: Platform_Error.EEXIST
EXDEV: Errno: 18 EXDEV :: Platform_Error.EXDEV
ENODEV: Errno: 19 ENODEV :: Platform_Error.ENODEV
ENOTDIR: Errno: 20 ENOTDIR :: Platform_Error.ENOTDIR
EISDIR: Errno: 21 EISDIR :: Platform_Error.EISDIR
EINVAL: Errno: 22 EINVAL :: Platform_Error.EINVAL
ENFILE: Errno: 23 ENFILE :: Platform_Error.ENFILE
EMFILE: Errno: 24 EMFILE :: Platform_Error.EMFILE
ENOTTY: Errno: 25 ENOTTY :: Platform_Error.ENOTTY
ETXTBSY: Errno: 26 ETXTBSY :: Platform_Error.ETXTBSY
EFBIG: Errno: 27 EFBIG :: Platform_Error.EFBIG
ENOSPC: Errno: 28 ENOSPC :: Platform_Error.ENOSPC
ESPIPE: Errno: 29 ESPIPE :: Platform_Error.ESPIPE
EROFS: Errno: 30 EROFS :: Platform_Error.EROFS
EMLINK: Errno: 31 EMLINK :: Platform_Error.EMLINK
EPIPE: Errno: 32 EPIPE :: Platform_Error.EPIPE
EDOM: Errno: 33 EDOM :: Platform_Error.EDOM
ERANGE: Errno: 34 ERANGE :: Platform_Error.ERANGE
EAGAIN: Errno: 35 EAGAIN :: Platform_Error.EAGAIN
EWOULDBLOCK: Errno: EAGAIN EWOULDBLOCK :: Platform_Error.EWOULDBLOCK
EINPROGRESS: Errno: 36 EINPROGRESS :: Platform_Error.EINPROGRESS
EALREADY: Errno: 37 EALREADY :: Platform_Error.EALREADY
ENOTSOCK: Errno: 38 ENOTSOCK :: Platform_Error.ENOTSOCK
EDESTADDRREQ: Errno: 39 EDESTADDRREQ :: Platform_Error.EDESTADDRREQ
EMSGSIZE: Errno: 40 EMSGSIZE :: Platform_Error.EMSGSIZE
EPROTOTYPE: Errno: 41 EPROTOTYPE :: Platform_Error.EPROTOTYPE
ENOPROTOOPT: Errno: 42 ENOPROTOOPT :: Platform_Error.ENOPROTOOPT
EPROTONOSUPPORT: Errno: 43 EPROTONOSUPPORT :: Platform_Error.EPROTONOSUPPORT
ESOCKTNOSUPPORT: Errno: 44 ESOCKTNOSUPPORT :: Platform_Error.ESOCKTNOSUPPORT
EOPNOTSUPP: Errno: 45 EOPNOTSUPP :: Platform_Error.EOPNOTSUPP
EPFNOSUPPORT: Errno: 46 EPFNOSUPPORT :: Platform_Error.EPFNOSUPPORT
EAFNOSUPPORT: Errno: 47 EAFNOSUPPORT :: Platform_Error.EAFNOSUPPORT
EADDRINUSE: Errno: 48 EADDRINUSE :: Platform_Error.EADDRINUSE
EADDRNOTAVAIL: Errno: 49 EADDRNOTAVAIL :: Platform_Error.EADDRNOTAVAIL
ENETDOWN: Errno: 50 ENETDOWN :: Platform_Error.ENETDOWN
ENETUNREACH: Errno: 51 ENETUNREACH :: Platform_Error.ENETUNREACH
ENETRESET: Errno: 52 ENETRESET :: Platform_Error.ENETRESET
ECONNABORTED: Errno: 53 ECONNABORTED :: Platform_Error.ECONNABORTED
ECONNRESET: Errno: 54 ECONNRESET :: Platform_Error.ECONNRESET
ENOBUFS: Errno: 55 ENOBUFS :: Platform_Error.ENOBUFS
EISCONN: Errno: 56 EISCONN :: Platform_Error.EISCONN
ENOTCONN: Errno: 57 ENOTCONN :: Platform_Error.ENOTCONN
ESHUTDOWN: Errno: 58 ESHUTDOWN :: Platform_Error.ESHUTDOWN
ETOOMANYREFS: Errno: 59 ETOOMANYREFS :: Platform_Error.ETOOMANYREFS
ETIMEDOUT: Errno: 60 ETIMEDOUT :: Platform_Error.ETIMEDOUT
ECONNREFUSED: Errno: 61 ECONNREFUSED :: Platform_Error.ECONNREFUSED
ELOOP: Errno: 62 ELOOP :: Platform_Error.ELOOP
ENAMETOOLONG: Errno: 63 ENAMETOOLONG :: Platform_Error.ENAMETOOLONG
EHOSTDOWN: Errno: 64 EHOSTDOWN :: Platform_Error.EHOSTDOWN
EHOSTUNREACH: Errno: 65 EHOSTUNREACH :: Platform_Error.EHOSTUNREACH
ENOTEMPTY: Errno: 66 ENOTEMPTY :: Platform_Error.ENOTEMPTY
EPROCLIM: Errno: 67 EPROCLIM :: Platform_Error.EPROCLIM
EUSERS: Errno: 68 EUSERS :: Platform_Error.EUSERS
EDQUOT: Errno: 69 EDQUOT :: Platform_Error.EDQUOT
ESTALE: Errno: 70 ESTALE :: Platform_Error.ESTALE
EREMOTE: Errno: 71 EREMOTE :: Platform_Error.EREMOTE
EBADRPC: Errno: 72 EBADRPC :: Platform_Error.EBADRPC
ERPCMISMATCH: Errno: 73 ERPCMISMATCH :: Platform_Error.ERPCMISMATCH
EPROGUNAVAIL: Errno: 74 EPROGUNAVAIL :: Platform_Error.EPROGUNAVAIL
EPROGMISMATCH: Errno: 75 EPROGMISMATCH :: Platform_Error.EPROGMISMATCH
EPROCUNAVAIL: Errno: 76 EPROCUNAVAIL :: Platform_Error.EPROCUNAVAIL
ENOLCK: Errno: 77 ENOLCK :: Platform_Error.ENOLCK
ENOSYS: Errno: 78 ENOSYS :: Platform_Error.ENOSYS
EFTYPE: Errno: 79 EFTYPE :: Platform_Error.EFTYPE
EAUTH: Errno: 80 EAUTH :: Platform_Error.EAUTH
ENEEDAUTH: Errno: 81 ENEEDAUTH :: Platform_Error.ENEEDAUTH
EIPSEC: Errno: 82 EIPSEC :: Platform_Error.EIPSEC
ENOATTR: Errno: 83 ENOATTR :: Platform_Error.ENOATTR
EILSEQ: Errno: 84 EILSEQ :: Platform_Error.EILSEQ
ENOMEDIUM: Errno: 85 ENOMEDIUM :: Platform_Error.ENOMEDIUM
EMEDIUMTYPE: Errno: 86 EMEDIUMTYPE :: Platform_Error.EMEDIUMTYPE
EOVERFLOW: Errno: 87 EOVERFLOW :: Platform_Error.EOVERFLOW
ECANCELED: Errno: 88 ECANCELED :: Platform_Error.ECANCELED
EIDRM: Errno: 89 EIDRM :: Platform_Error.EIDRM
ENOMSG: Errno: 90 ENOMSG :: Platform_Error.ENOMSG
ENOTSUP: Errno: 91 ENOTSUP :: Platform_Error.ENOTSUP
EBADMSG: Errno: 92 EBADMSG :: Platform_Error.EBADMSG
ENOTRECOVERABLE: Errno: 93 ENOTRECOVERABLE :: Platform_Error.ENOTRECOVERABLE
EOWNERDEAD: Errno: 94 EOWNERDEAD :: Platform_Error.EOWNERDEAD
EPROTO: Errno: 95 EPROTO :: Platform_Error.EPROTO
O_RDONLY :: 0x00000 O_RDONLY :: 0x00000
O_WRONLY :: 0x00001 O_WRONLY :: 0x00001
@@ -225,13 +322,13 @@ S_ISUID :: 0o4000 // Set user id on execution
S_ISGID :: 0o2000 // Set group id on execution S_ISGID :: 0o2000 // Set group id on execution
S_ISTXT :: 0o1000 // Sticky bit S_ISTXT :: 0o1000 // Sticky bit
S_ISLNK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFLNK } @(require_results) S_ISLNK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFLNK }
S_ISREG :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFREG } @(require_results) S_ISREG :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFREG }
S_ISDIR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFDIR } @(require_results) S_ISDIR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFDIR }
S_ISCHR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFCHR } @(require_results) S_ISCHR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFCHR }
S_ISBLK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFBLK } @(require_results) S_ISBLK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFBLK }
S_ISFIFO :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFIFO } @(require_results) S_ISFIFO :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFIFO }
S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK } @(require_results) S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK }
F_OK :: 0x00 // Test for file existance F_OK :: 0x00 // Test for file existance
X_OK :: 0x01 // Test for execute permission X_OK :: 0x01 // Test for execute permission
@@ -291,38 +388,47 @@ foreign libc {
@(link_name="dlerror") _unix_dlerror :: proc() -> cstring --- @(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---
} }
@(require_results)
is_path_separator :: proc(r: rune) -> bool { is_path_separator :: proc(r: rune) -> bool {
return r == '/' return r == '/'
} }
get_last_error :: proc "contextless" () -> int { @(require_results, no_instrumentation)
return int(__error()^) get_last_error :: proc "contextless" () -> Error {
return Platform_Error(__error()^)
} }
fork :: proc() -> (Pid, Errno) { @(require_results)
fork :: proc() -> (Pid, Error) {
pid := _unix_fork() pid := _unix_fork()
if pid == -1 { if pid == -1 {
return Pid(-1), Errno(get_last_error()) return Pid(-1), get_last_error()
} }
return Pid(pid), ERROR_NONE return Pid(pid), nil
} }
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) { @(require_results)
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
handle := _unix_open(cstr, c.int(flags), c.int(mode)) handle := _unix_open(cstr, c.int(flags), c.int(mode))
if handle == -1 { if handle == -1 {
return INVALID_HANDLE, Errno(get_last_error()) return INVALID_HANDLE, get_last_error()
} }
return handle, ERROR_NONE return handle, nil
} }
close :: proc(fd: Handle) -> Errno { close :: proc(fd: Handle) -> Error {
result := _unix_close(fd) result := _unix_close(fd)
if result == -1 { if result == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
}
flush :: proc(fd: Handle) -> Error {
// do nothing
return nil
} }
// If you read or write more than `SSIZE_MAX` bytes, OpenBSD returns `EINVAL`. // If you read or write more than `SSIZE_MAX` bytes, OpenBSD returns `EINVAL`.
@@ -333,124 +439,148 @@ close :: proc(fd: Handle) -> Errno {
@(private) @(private)
MAX_RW :: 1 << 30 MAX_RW :: 1 << 30
read :: proc(fd: Handle, data: []byte) -> (int, Errno) { read :: proc(fd: Handle, data: []byte) -> (int, Error) {
to_read := min(c.size_t(len(data)), MAX_RW) to_read := min(c.size_t(len(data)), MAX_RW)
bytes_read := _unix_read(fd, &data[0], to_read) bytes_read := _unix_read(fd, &data[0], to_read)
if bytes_read == -1 { if bytes_read == -1 {
return -1, Errno(get_last_error()) return -1, get_last_error()
} }
return int(bytes_read), ERROR_NONE return int(bytes_read), nil
} }
write :: proc(fd: Handle, data: []byte) -> (int, Errno) { write :: proc(fd: Handle, data: []byte) -> (int, Error) {
if len(data) == 0 { if len(data) == 0 {
return 0, ERROR_NONE return 0, nil
} }
to_write := min(c.size_t(len(data)), MAX_RW) to_write := min(c.size_t(len(data)), MAX_RW)
bytes_written := _unix_write(fd, &data[0], to_write) bytes_written := _unix_write(fd, &data[0], to_write)
if bytes_written == -1 { if bytes_written == -1 {
return -1, Errno(get_last_error()) return -1, get_last_error()
} }
return int(bytes_written), ERROR_NONE return int(bytes_written), nil
} }
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
curr := seek(fd, offset, SEEK_CUR) or_return
n, err = read(fd, data)
_, err1 := seek(fd, curr, SEEK_SET)
if err1 != nil && err == nil {
err = err1
}
return
}
write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (n: int, err: Error) {
curr := seek(fd, offset, SEEK_CUR) or_return
n, err = write(fd, data)
_, err1 := seek(fd, curr, SEEK_SET)
if err1 != nil && err == nil {
err = err1
}
return
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
res := _unix_seek(fd, offset, c.int(whence)) res := _unix_seek(fd, offset, c.int(whence))
if res == -1 { if res == -1 {
return -1, Errno(get_last_error()) return -1, get_last_error()
} }
return res, ERROR_NONE return res, nil
} }
file_size :: proc(fd: Handle) -> (i64, Errno) { @(require_results)
s, err := _fstat(fd) file_size :: proc(fd: Handle) -> (size: i64, err: Error) {
if err != ERROR_NONE { size = -1
return -1, err s := _fstat(fd) or_return
} size = s.size
return s.size, ERROR_NONE return
} }
rename :: proc(old_path, new_path: string) -> Errno { rename :: proc(old_path, new_path: string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator) old_path_cstr := strings.clone_to_cstring(old_path, context.temp_allocator)
new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator) new_path_cstr := strings.clone_to_cstring(new_path, context.temp_allocator)
res := _unix_rename(old_path_cstr, new_path_cstr) res := _unix_rename(old_path_cstr, new_path_cstr)
if res == -1 { if res == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
remove :: proc(path: string) -> Errno { remove :: proc(path: string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator) path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_unlink(path_cstr) res := _unix_unlink(path_cstr)
if res == -1 { if res == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
make_directory :: proc(path: string, mode: mode_t = 0o775) -> Errno { make_directory :: proc(path: string, mode: mode_t = 0o775) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator) path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_mkdir(path_cstr, mode) res := _unix_mkdir(path_cstr, mode)
if res == -1 { if res == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
remove_directory :: proc(path: string) -> Errno { remove_directory :: proc(path: string) -> Error {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
path_cstr := strings.clone_to_cstring(path, context.temp_allocator) path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_rmdir(path_cstr) res := _unix_rmdir(path_cstr)
if res == -1 { if res == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
@(require_results)
is_file_handle :: proc(fd: Handle) -> bool { is_file_handle :: proc(fd: Handle) -> bool {
s, err := _fstat(fd) s, err := _fstat(fd)
if err != ERROR_NONE { if err != nil {
return false return false
} }
return S_ISREG(s.mode) return S_ISREG(s.mode)
} }
@(require_results)
is_file_path :: proc(path: string, follow_links: bool = true) -> bool { is_file_path :: proc(path: string, follow_links: bool = true) -> bool {
s: OS_Stat s: OS_Stat
err: Errno err: Error
if follow_links { if follow_links {
s, err = _stat(path) s, err = _stat(path)
} else { } else {
s, err = _lstat(path) s, err = _lstat(path)
} }
if err != ERROR_NONE { if err != nil {
return false return false
} }
return S_ISREG(s.mode) return S_ISREG(s.mode)
} }
@(require_results)
is_dir_handle :: proc(fd: Handle) -> bool { is_dir_handle :: proc(fd: Handle) -> bool {
s, err := _fstat(fd) s, err := _fstat(fd)
if err != ERROR_NONE { if err != nil {
return false return false
} }
return S_ISDIR(s.mode) return S_ISDIR(s.mode)
} }
@(require_results)
is_dir_path :: proc(path: string, follow_links: bool = true) -> bool { is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
s: OS_Stat s: OS_Stat
err: Errno err: Error
if follow_links { if follow_links {
s, err = _stat(path) s, err = _stat(path)
} else { } else {
s, err = _lstat(path) s, err = _lstat(path)
} }
if err != ERROR_NONE { if err != nil {
return false return false
} }
return S_ISDIR(s.mode) return S_ISDIR(s.mode)
@@ -469,26 +599,22 @@ stderr: Handle = 2
last_write_time :: proc(fd: Handle) -> File_Time {} last_write_time :: proc(fd: Handle) -> File_Time {}
last_write_time_by_name :: proc(name: string) -> File_Time {} last_write_time_by_name :: proc(name: string) -> File_Time {}
*/ */
last_write_time :: proc(fd: Handle) -> (File_Time, Errno) { @(require_results)
s, err := _fstat(fd) last_write_time :: proc(fd: Handle) -> (time: File_Time, err: Error) {
if err != ERROR_NONE { s := _fstat(fd) or_return
return 0, err
}
modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
return File_Time(modified), ERROR_NONE return File_Time(modified), nil
} }
last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) { @(require_results)
s, err := _stat(name) last_write_time_by_name :: proc(name: string) -> (time: File_Time, err: Error) {
if err != ERROR_NONE { s := _stat(name) or_return
return 0, err
}
modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
return File_Time(modified), ERROR_NONE return File_Time(modified), nil
} }
@private @(private, require_results)
_stat :: proc(path: string) -> (OS_Stat, Errno) { _stat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -496,13 +622,13 @@ _stat :: proc(path: string) -> (OS_Stat, Errno) {
s: OS_Stat = --- s: OS_Stat = ---
res := _unix_stat(cstr, &s) res := _unix_stat(cstr, &s)
if res == -1 { if res == -1 {
return s, Errno(get_last_error()) return s, get_last_error()
} }
return s, ERROR_NONE return s, nil
} }
@private @(private, require_results)
_lstat :: proc(path: string) -> (OS_Stat, Errno) { _lstat :: proc(path: string) -> (OS_Stat, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -510,55 +636,55 @@ _lstat :: proc(path: string) -> (OS_Stat, Errno) {
s: OS_Stat = --- s: OS_Stat = ---
res := _unix_lstat(cstr, &s) res := _unix_lstat(cstr, &s)
if res == -1 { if res == -1 {
return s, Errno(get_last_error()) return s, get_last_error()
} }
return s, ERROR_NONE return s, nil
} }
@private @(private, require_results)
_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) { _fstat :: proc(fd: Handle) -> (OS_Stat, Error) {
// deliberately uninitialized // deliberately uninitialized
s: OS_Stat = --- s: OS_Stat = ---
res := _unix_fstat(fd, &s) res := _unix_fstat(fd, &s)
if res == -1 { if res == -1 {
return s, Errno(get_last_error()) return s, get_last_error()
} }
return s, ERROR_NONE return s, nil
} }
@private @(private, require_results)
_fdopendir :: proc(fd: Handle) -> (Dir, Errno) { _fdopendir :: proc(fd: Handle) -> (Dir, Error) {
dirp := _unix_fdopendir(fd) dirp := _unix_fdopendir(fd)
if dirp == cast(Dir)nil { if dirp == cast(Dir)nil {
return nil, Errno(get_last_error()) return nil, get_last_error()
} }
return dirp, ERROR_NONE return dirp, nil
} }
@private @(private)
_closedir :: proc(dirp: Dir) -> Errno { _closedir :: proc(dirp: Dir) -> Error {
rc := _unix_closedir(dirp) rc := _unix_closedir(dirp)
if rc != 0 { if rc != 0 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
@private @(private)
_rewinddir :: proc(dirp: Dir) { _rewinddir :: proc(dirp: Dir) {
_unix_rewinddir(dirp) _unix_rewinddir(dirp)
} }
@private @(private, require_results)
_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) { _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Error, end_of_stream: bool) {
result: ^Dirent result: ^Dirent
rc := _unix_readdir_r(dirp, &entry, &result) rc := _unix_readdir_r(dirp, &entry, &result)
if rc != 0 { if rc != 0 {
err = Errno(get_last_error()) err = get_last_error()
return return
} }
err = ERROR_NONE err = nil
if result == nil { if result == nil {
end_of_stream = true end_of_stream = true
@@ -568,8 +694,8 @@ _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool)
return return
} }
@private @(private, require_results)
_readlink :: proc(path: string) -> (string, Errno) { _readlink :: proc(path: string) -> (string, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator) runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
path_cstr := strings.clone_to_cstring(path, context.temp_allocator) path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
@@ -579,23 +705,25 @@ _readlink :: proc(path: string) -> (string, Errno) {
rc := _unix_readlink(path_cstr, &(buf[0]), bufsz) rc := _unix_readlink(path_cstr, &(buf[0]), bufsz)
if rc == -1 { if rc == -1 {
delete(buf) delete(buf)
return "", Errno(get_last_error()) return "", get_last_error()
} else if rc == int(bufsz) { } else if rc == int(bufsz) {
bufsz += MAX_PATH bufsz += MAX_PATH
delete(buf) delete(buf)
buf = make([]byte, bufsz) buf = make([]byte, bufsz)
} else { } else {
return strings.string_from_ptr(&buf[0], rc), ERROR_NONE return strings.string_from_ptr(&buf[0], rc), nil
} }
} }
} }
// XXX OpenBSD // XXX OpenBSD
absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) { @(require_results)
return "", Errno(ENOSYS) absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {
return "", Error(ENOSYS)
} }
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) { @(require_results)
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
rel := rel rel := rel
if rel == "" { if rel == "" {
rel = "." rel = "."
@@ -606,25 +734,26 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
path_ptr := _unix_realpath(rel_cstr, nil) path_ptr := _unix_realpath(rel_cstr, nil)
if path_ptr == nil { if path_ptr == nil {
return "", Errno(get_last_error()) return "", get_last_error()
} }
defer _unix_free(path_ptr) defer _unix_free(path_ptr)
path = strings.clone(string(cstring(path_ptr))) path = strings.clone(string(cstring(path_ptr)))
return path, ERROR_NONE return path, nil
} }
access :: proc(path: string, mask: int) -> (bool, Errno) { access :: proc(path: string, mask: int) -> (bool, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_access(cstr, c.int(mask)) res := _unix_access(cstr, c.int(mask))
if res == -1 { if res == -1 {
return false, Errno(get_last_error()) return false, get_last_error()
} }
return true, ERROR_NONE return true, nil
} }
@(require_results)
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator) runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
path_str := strings.clone_to_cstring(key, context.temp_allocator) path_str := strings.clone_to_cstring(key, context.temp_allocator)
@@ -635,11 +764,13 @@ lookup_env :: proc(key: string, allocator := context.allocator) -> (value: strin
return strings.clone(string(cstr), allocator), true return strings.clone(string(cstr), allocator), true
} }
@(require_results)
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) { get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
value, _ = lookup_env(key, allocator) value, _ = lookup_env(key, allocator)
return return
} }
@(require_results)
get_current_directory :: proc() -> string { get_current_directory :: proc() -> string {
buf := make([dynamic]u8, MAX_PATH) buf := make([dynamic]u8, MAX_PATH)
for { for {
@@ -647,7 +778,7 @@ get_current_directory :: proc() -> string {
if cwd != nil { if cwd != nil {
return string(cwd) return string(cwd)
} }
if Errno(get_last_error()) != ERANGE { if get_last_error() != ERANGE {
delete(buf) delete(buf)
return "" return ""
} }
@@ -656,14 +787,14 @@ get_current_directory :: proc() -> string {
unreachable() unreachable()
} }
set_current_directory :: proc(path: string) -> (err: Errno) { set_current_directory :: proc(path: string) -> (err: Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator) cstr := strings.clone_to_cstring(path, context.temp_allocator)
res := _unix_chdir(cstr) res := _unix_chdir(cstr)
if res == -1 { if res == -1 {
return Errno(get_last_error()) return get_last_error()
} }
return ERROR_NONE return nil
} }
exit :: proc "contextless" (code: int) -> ! { exit :: proc "contextless" (code: int) -> ! {
@@ -671,16 +802,19 @@ exit :: proc "contextless" (code: int) -> ! {
_unix_exit(c.int(code)) _unix_exit(c.int(code))
} }
@(require_results)
current_thread_id :: proc "contextless" () -> int { current_thread_id :: proc "contextless" () -> int {
return _unix_getthrid() return _unix_getthrid()
} }
@(require_results)
dlopen :: proc(filename: string, flags: int) -> rawptr { dlopen :: proc(filename: string, flags: int) -> rawptr {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(filename, context.temp_allocator) cstr := strings.clone_to_cstring(filename, context.temp_allocator)
handle := _unix_dlopen(cstr, c.int(flags)) handle := _unix_dlopen(cstr, c.int(flags))
return handle return handle
} }
@(require_results)
dlsym :: proc(handle: rawptr, symbol: string) -> rawptr { dlsym :: proc(handle: rawptr, symbol: string) -> rawptr {
assert(handle != nil) assert(handle != nil)
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
@@ -692,10 +826,12 @@ dlclose :: proc(handle: rawptr) -> bool {
assert(handle != nil) assert(handle != nil)
return _unix_dlclose(handle) == 0 return _unix_dlclose(handle) == 0
} }
@(require_results)
dlerror :: proc() -> string { dlerror :: proc() -> string {
return string(_unix_dlerror()) return string(_unix_dlerror())
} }
@(require_results)
get_page_size :: proc() -> int { get_page_size :: proc() -> int {
// NOTE(tetra): The page size never changes, so why do anything complicated // NOTE(tetra): The page size never changes, so why do anything complicated
// if we don't have to. // if we don't have to.
@@ -710,11 +846,12 @@ get_page_size :: proc() -> int {
_SC_NPROCESSORS_ONLN :: 503 _SC_NPROCESSORS_ONLN :: 503
@(private) @(private, require_results)
_processor_core_count :: proc() -> int { _processor_core_count :: proc() -> int {
return int(_sysconf(_SC_NPROCESSORS_ONLN)) return int(_sysconf(_SC_NPROCESSORS_ONLN))
} }
@(require_results)
_alloc_command_line_arguments :: proc() -> []string { _alloc_command_line_arguments :: proc() -> []string {
res := make([]string, len(runtime.args__)) res := make([]string, len(runtime.args__))
for arg, i in runtime.args__ { for arg, i in runtime.args__ {
+25 -18
View File
@@ -4,12 +4,10 @@ import "core:sys/wasm/wasi"
import "base:runtime" import "base:runtime"
Handle :: distinct i32 Handle :: distinct i32
Errno :: distinct i32 _Platform_Error :: wasi.errno_t
INVALID_HANDLE :: -1 INVALID_HANDLE :: -1
ERROR_NONE :: Errno(wasi.errno_t.SUCCESS)
O_RDONLY :: 0x00000 O_RDONLY :: 0x00000
O_WRONLY :: 0x00001 O_WRONLY :: 0x00001
O_RDWR :: 0x00002 O_RDWR :: 0x00002
@@ -29,6 +27,7 @@ stderr: Handle = 2
args := _alloc_command_line_arguments() args := _alloc_command_line_arguments()
@(require_results)
_alloc_command_line_arguments :: proc() -> (args: []string) { _alloc_command_line_arguments :: proc() -> (args: []string) {
args = make([]string, len(runtime.args__)) args = make([]string, len(runtime.args__))
for &arg, i in args { for &arg, i in args {
@@ -93,8 +92,9 @@ init_preopens :: proc() {
preopens = dyn_preopens[:] preopens = dyn_preopens[:]
} }
@(require_results)
wasi_match_preopen :: proc(path: string) -> (wasi.fd_t, string, bool) { wasi_match_preopen :: proc(path: string) -> (wasi.fd_t, string, bool) {
@(require_results)
prefix_matches :: proc(prefix, path: string) -> bool { prefix_matches :: proc(prefix, path: string) -> bool {
// Empty is valid for any relative path. // Empty is valid for any relative path.
if len(prefix) == 0 && len(path) > 0 && path[0] != '/' { if len(prefix) == 0 && len(path) > 0 && path[0] != '/' {
@@ -148,23 +148,24 @@ wasi_match_preopen :: proc(path: string) -> (wasi.fd_t, string, bool) {
write :: proc(fd: Handle, data: []byte) -> (int, Errno) { write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
iovs := wasi.ciovec_t(data) iovs := wasi.ciovec_t(data)
n, err := wasi.fd_write(wasi.fd_t(fd), {iovs}) n, err := wasi.fd_write(wasi.fd_t(fd), {iovs})
return int(n), Errno(err) return int(n), Platform_Error(err)
} }
read :: proc(fd: Handle, data: []byte) -> (int, Errno) { read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
iovs := wasi.iovec_t(data) iovs := wasi.iovec_t(data)
n, err := wasi.fd_read(wasi.fd_t(fd), {iovs}) n, err := wasi.fd_read(wasi.fd_t(fd), {iovs})
return int(n), Errno(err) return int(n), Platform_Error(err)
} }
write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) { write_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
iovs := wasi.ciovec_t(data) iovs := wasi.ciovec_t(data)
n, err := wasi.fd_pwrite(wasi.fd_t(fd), {iovs}, wasi.filesize_t(offset)) n, err := wasi.fd_pwrite(wasi.fd_t(fd), {iovs}, wasi.filesize_t(offset))
return int(n), Errno(err) return int(n), Platform_Error(err)
} }
read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) { read_at :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
iovs := wasi.iovec_t(data) iovs := wasi.iovec_t(data)
n, err := wasi.fd_pread(wasi.fd_t(fd), {iovs}, wasi.filesize_t(offset)) n, err := wasi.fd_pread(wasi.fd_t(fd), {iovs}, wasi.filesize_t(offset))
return int(n), Errno(err) return int(n), Platform_Error(err)
} }
@(require_results)
open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) { open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) {
oflags: wasi.oflags_t oflags: wasi.oflags_t
if mode & O_CREATE == O_CREATE { if mode & O_CREATE == O_CREATE {
@@ -201,30 +202,36 @@ open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errn
} }
fd, err := wasi.path_open(dir_fd, {.SYMLINK_FOLLOW}, relative, oflags, rights, {}, fdflags) fd, err := wasi.path_open(dir_fd, {.SYMLINK_FOLLOW}, relative, oflags, rights, {}, fdflags)
return Handle(fd), Errno(err) return Handle(fd), Platform_Error(err)
} }
close :: proc(fd: Handle) -> Errno { close :: proc(fd: Handle) -> Errno {
err := wasi.fd_close(wasi.fd_t(fd)) err := wasi.fd_close(wasi.fd_t(fd))
return Errno(err) return Platform_Error(err)
} }
flush :: proc(fd: Handle) -> Error {
// do nothing
return nil
}
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
n, err := wasi.fd_seek(wasi.fd_t(fd), wasi.filedelta_t(offset), wasi.whence_t(whence)) n, err := wasi.fd_seek(wasi.fd_t(fd), wasi.filedelta_t(offset), wasi.whence_t(whence))
return i64(n), Errno(err) return i64(n), Platform_Error(err)
} }
@(require_results)
current_thread_id :: proc "contextless" () -> int { current_thread_id :: proc "contextless" () -> int {
return 0 return 0
} }
@(private) @(private, require_results)
_processor_core_count :: proc() -> int { _processor_core_count :: proc() -> int {
return 1 return 1
} }
file_size :: proc(fd: Handle) -> (i64, Errno) { @(require_results)
stat, err := wasi.fd_filestat_get(wasi.fd_t(fd)) file_size :: proc(fd: Handle) -> (size: i64, err: Errno) {
if err != nil { stat := wasi.fd_filestat_get(wasi.fd_t(fd)) or_return
return 0, Errno(err) size = i64(stat.size)
} return
return i64(stat.size), 0
} }
+106 -48
View File
@@ -7,7 +7,6 @@ import "base:intrinsics"
Handle :: distinct uintptr Handle :: distinct uintptr
File_Time :: distinct u64 File_Time :: distinct u64
Errno :: distinct int
INVALID_HANDLE :: ~Handle(0) INVALID_HANDLE :: ~Handle(0)
@@ -27,70 +26,119 @@ O_SYNC :: 0x01000
O_ASYNC :: 0x02000 O_ASYNC :: 0x02000
O_CLOEXEC :: 0x80000 O_CLOEXEC :: 0x80000
_Platform_Error :: win32.System_Error
ERROR_NONE: Errno : 0 ERROR_FILE_NOT_FOUND :: _Platform_Error(2)
ERROR_FILE_NOT_FOUND: Errno : 2 ERROR_PATH_NOT_FOUND :: _Platform_Error(3)
ERROR_PATH_NOT_FOUND: Errno : 3 ERROR_ACCESS_DENIED :: _Platform_Error(5)
ERROR_ACCESS_DENIED: Errno : 5 ERROR_INVALID_HANDLE :: _Platform_Error(6)
ERROR_INVALID_HANDLE: Errno : 6 ERROR_NOT_ENOUGH_MEMORY :: _Platform_Error(8)
ERROR_NOT_ENOUGH_MEMORY: Errno : 8 ERROR_NO_MORE_FILES :: _Platform_Error(18)
ERROR_NO_MORE_FILES: Errno : 18 ERROR_HANDLE_EOF :: _Platform_Error(38)
ERROR_HANDLE_EOF: Errno : 38 ERROR_NETNAME_DELETED :: _Platform_Error(64)
ERROR_NETNAME_DELETED: Errno : 64 ERROR_FILE_EXISTS :: _Platform_Error(80)
ERROR_FILE_EXISTS: Errno : 80 ERROR_INVALID_PARAMETER :: _Platform_Error(87)
ERROR_INVALID_PARAMETER: Errno : 87 ERROR_BROKEN_PIPE :: _Platform_Error(109)
ERROR_BROKEN_PIPE: Errno : 109 ERROR_BUFFER_OVERFLOW :: _Platform_Error(111)
ERROR_BUFFER_OVERFLOW: Errno : 111 ERROR_INSUFFICIENT_BUFFER :: _Platform_Error(122)
ERROR_INSUFFICIENT_BUFFER: Errno : 122 ERROR_MOD_NOT_FOUND :: _Platform_Error(126)
ERROR_MOD_NOT_FOUND: Errno : 126 ERROR_PROC_NOT_FOUND :: _Platform_Error(127)
ERROR_PROC_NOT_FOUND: Errno : 127 ERROR_DIR_NOT_EMPTY :: _Platform_Error(145)
ERROR_DIR_NOT_EMPTY: Errno : 145 ERROR_ALREADY_EXISTS :: _Platform_Error(183)
ERROR_ALREADY_EXISTS: Errno : 183 ERROR_ENVVAR_NOT_FOUND :: _Platform_Error(203)
ERROR_ENVVAR_NOT_FOUND: Errno : 203 ERROR_MORE_DATA :: _Platform_Error(234)
ERROR_MORE_DATA: Errno : 234 ERROR_OPERATION_ABORTED :: _Platform_Error(995)
ERROR_OPERATION_ABORTED: Errno : 995 ERROR_IO_PENDING :: _Platform_Error(997)
ERROR_IO_PENDING: Errno : 997 ERROR_NOT_FOUND :: _Platform_Error(1168)
ERROR_NOT_FOUND: Errno : 1168 ERROR_PRIVILEGE_NOT_HELD :: _Platform_Error(1314)
ERROR_PRIVILEGE_NOT_HELD: Errno : 1314 WSAEACCES :: _Platform_Error(10013)
WSAEACCES: Errno : 10013 WSAECONNRESET :: _Platform_Error(10054)
WSAECONNRESET: Errno : 10054
// Windows reserves errors >= 1<<29 for application use ERROR_FILE_IS_PIPE :: General_Error.File_Is_Pipe
ERROR_FILE_IS_PIPE: Errno : 1<<29 + 0 ERROR_FILE_IS_NOT_DIR :: General_Error.Not_Dir
ERROR_FILE_IS_NOT_DIR: Errno : 1<<29 + 1
ERROR_NEGATIVE_OFFSET: Errno : 1<<29 + 2
// "Argv" arguments converted to Odin strings // "Argv" arguments converted to Odin strings
args := _alloc_command_line_arguments() args := _alloc_command_line_arguments()
@(require_results, no_instrumentation)
get_last_error :: proc "contextless" () -> 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_HANDLE_EOF:
return .EOF
case win32.ERROR_INVALID_HANDLE:
return .Invalid_File
case
win32.ERROR_BAD_ARGUMENTS,
win32.ERROR_INVALID_PARAMETER,
win32.ERROR_NOT_ENOUGH_MEMORY,
win32.ERROR_NO_MORE_FILES,
win32.ERROR_LOCK_VIOLATION,
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)
}
@(require_results)
last_write_time :: proc(fd: Handle) -> (File_Time, Error) {
last_write_time :: proc(fd: Handle) -> (File_Time, Errno) {
file_info: win32.BY_HANDLE_FILE_INFORMATION file_info: win32.BY_HANDLE_FILE_INFORMATION
if !win32.GetFileInformationByHandle(win32.HANDLE(fd), &file_info) { if !win32.GetFileInformationByHandle(win32.HANDLE(fd), &file_info) {
return 0, Errno(win32.GetLastError()) return 0, get_last_error()
} }
lo := File_Time(file_info.ftLastWriteTime.dwLowDateTime) lo := File_Time(file_info.ftLastWriteTime.dwLowDateTime)
hi := File_Time(file_info.ftLastWriteTime.dwHighDateTime) hi := File_Time(file_info.ftLastWriteTime.dwHighDateTime)
return lo | hi << 32, ERROR_NONE return lo | hi << 32, nil
} }
last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) { @(require_results)
last_write_time_by_name :: proc(name: string) -> (File_Time, Error) {
data: win32.WIN32_FILE_ATTRIBUTE_DATA data: win32.WIN32_FILE_ATTRIBUTE_DATA
wide_path := win32.utf8_to_wstring(name) wide_path := win32.utf8_to_wstring(name)
if !win32.GetFileAttributesExW(wide_path, win32.GetFileExInfoStandard, &data) { if !win32.GetFileAttributesExW(wide_path, win32.GetFileExInfoStandard, &data) {
return 0, Errno(win32.GetLastError()) return 0, get_last_error()
} }
l := File_Time(data.ftLastWriteTime.dwLowDateTime) l := File_Time(data.ftLastWriteTime.dwLowDateTime)
h := File_Time(data.ftLastWriteTime.dwHighDateTime) h := File_Time(data.ftLastWriteTime.dwHighDateTime)
return l | h << 32, ERROR_NONE return l | h << 32, nil
} }
@(require_results)
get_page_size :: proc() -> int { get_page_size :: proc() -> int {
// NOTE(tetra): The page size never changes, so why do anything complicated // NOTE(tetra): The page size never changes, so why do anything complicated
// if we don't have to. // if we don't have to.
@@ -105,7 +153,7 @@ get_page_size :: proc() -> int {
return page_size return page_size
} }
@(private) @(private, require_results)
_processor_core_count :: proc() -> int { _processor_core_count :: proc() -> int {
length : win32.DWORD = 0 length : win32.DWORD = 0
result := win32.GetLogicalProcessorInformation(nil, &length) result := win32.GetLogicalProcessorInformation(nil, &length)
@@ -136,12 +184,14 @@ exit :: proc "contextless" (code: int) -> ! {
@(require_results)
current_thread_id :: proc "contextless" () -> int { current_thread_id :: proc "contextless" () -> int {
return int(win32.GetCurrentThreadId()) return int(win32.GetCurrentThreadId())
} }
@(require_results)
_alloc_command_line_arguments :: proc() -> []string { _alloc_command_line_arguments :: proc() -> []string {
arg_count: i32 arg_count: i32
arg_list_ptr := win32.CommandLineToArgvW(win32.GetCommandLineW(), &arg_count) arg_list_ptr := win32.CommandLineToArgvW(win32.GetCommandLineW(), &arg_count)
@@ -175,44 +225,52 @@ _alloc_command_line_arguments :: proc() -> []string {
*/ */
WINDOWS_11_BUILD_CUTOFF :: 22_000 WINDOWS_11_BUILD_CUTOFF :: 22_000
get_windows_version_w :: proc() -> win32.OSVERSIONINFOEXW { @(require_results)
get_windows_version_w :: proc "contextless" () -> win32.OSVERSIONINFOEXW {
osvi : win32.OSVERSIONINFOEXW osvi : win32.OSVERSIONINFOEXW
osvi.dwOSVersionInfoSize = size_of(win32.OSVERSIONINFOEXW) osvi.dwOSVersionInfoSize = size_of(win32.OSVERSIONINFOEXW)
win32.RtlGetVersion(&osvi) win32.RtlGetVersion(&osvi)
return osvi return osvi
} }
is_windows_xp :: proc() -> bool { @(require_results)
is_windows_xp :: proc "contextless" () -> bool {
osvi := get_windows_version_w() osvi := get_windows_version_w()
return (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) return (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1)
} }
is_windows_vista :: proc() -> bool { @(require_results)
is_windows_vista :: proc "contextless" () -> bool {
osvi := get_windows_version_w() osvi := get_windows_version_w()
return (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 0) return (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 0)
} }
is_windows_7 :: proc() -> bool { @(require_results)
is_windows_7 :: proc "contextless" () -> bool {
osvi := get_windows_version_w() osvi := get_windows_version_w()
return (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 1) return (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 1)
} }
is_windows_8 :: proc() -> bool { @(require_results)
is_windows_8 :: proc "contextless" () -> bool {
osvi := get_windows_version_w() osvi := get_windows_version_w()
return (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 2) return (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 2)
} }
is_windows_8_1 :: proc() -> bool { @(require_results)
is_windows_8_1 :: proc "contextless" () -> bool {
osvi := get_windows_version_w() osvi := get_windows_version_w()
return (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 3) return (osvi.dwMajorVersion == 6 && osvi.dwMinorVersion == 3)
} }
is_windows_10 :: proc() -> bool { @(require_results)
is_windows_10 :: proc "contextless" () -> bool {
osvi := get_windows_version_w() osvi := get_windows_version_w()
return (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber < WINDOWS_11_BUILD_CUTOFF) return (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber < WINDOWS_11_BUILD_CUTOFF)
} }
is_windows_11 :: proc() -> bool { @(require_results)
is_windows_11 :: proc "contextless" () -> bool {
osvi := get_windows_version_w() osvi := get_windows_version_w()
return (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber >= WINDOWS_11_BUILD_CUTOFF) return (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber >= WINDOWS_11_BUILD_CUTOFF)
} }
+18 -38
View File
@@ -50,14 +50,14 @@ File_Info :: struct {
} }
*/ */
@private @(private, require_results)
_make_time_from_unix_file_time :: proc(uft: Unix_File_Time) -> time.Time { _make_time_from_unix_file_time :: proc(uft: Unix_File_Time) -> time.Time {
return time.Time{ return time.Time{
_nsec = uft.nanoseconds + uft.seconds * 1_000_000_000, _nsec = uft.nanoseconds + uft.seconds * 1_000_000_000,
} }
} }
@private @(private)
_fill_file_info_from_stat :: proc(fi: ^File_Info, s: OS_Stat) { _fill_file_info_from_stat :: proc(fi: ^File_Info, s: OS_Stat) {
fi.size = s.size fi.size = s.size
fi.mode = cast(File_Mode)s.mode fi.mode = cast(File_Mode)s.mode
@@ -71,7 +71,7 @@ _fill_file_info_from_stat :: proc(fi: ^File_Info, s: OS_Stat) {
} }
@private @(private, require_results)
path_base :: proc(path: string) -> string { path_base :: proc(path: string) -> string {
is_separator :: proc(c: byte) -> bool { is_separator :: proc(c: byte) -> bool {
return c == '/' return c == '/'
@@ -100,55 +100,35 @@ path_base :: proc(path: string) -> string {
} }
lstat :: proc(name: string, allocator := context.allocator) -> (fi: File_Info, err: Errno) { @(require_results)
lstat :: proc(name: string, allocator := context.allocator) -> (fi: File_Info, err: Error) {
context.allocator = allocator context.allocator = allocator
s: OS_Stat s := _lstat(name) or_return
s, err = _lstat(name)
if err != ERROR_NONE {
return fi, err
}
_fill_file_info_from_stat(&fi, s) _fill_file_info_from_stat(&fi, s)
fi.fullpath, err = absolute_path_from_relative(name) fi.fullpath = absolute_path_from_relative(name) or_return
if err != ERROR_NONE {
return
}
fi.name = path_base(fi.fullpath) fi.name = path_base(fi.fullpath)
return fi, ERROR_NONE return
} }
stat :: proc(name: string, allocator := context.allocator) -> (fi: File_Info, err: Errno) { @(require_results)
stat :: proc(name: string, allocator := context.allocator) -> (fi: File_Info, err: Error) {
context.allocator = allocator context.allocator = allocator
s: OS_Stat s := _stat(name) or_return
s, err = _stat(name)
if err != ERROR_NONE {
return fi, err
}
_fill_file_info_from_stat(&fi, s) _fill_file_info_from_stat(&fi, s)
fi.fullpath, err = absolute_path_from_relative(name) fi.fullpath = absolute_path_from_relative(name) or_return
if err != ERROR_NONE {
return
}
fi.name = path_base(fi.fullpath) fi.name = path_base(fi.fullpath)
return fi, ERROR_NONE return
} }
fstat :: proc(fd: Handle, allocator := context.allocator) -> (fi: File_Info, err: Errno) { @(require_results)
fstat :: proc(fd: Handle, allocator := context.allocator) -> (fi: File_Info, err: Error) {
context.allocator = allocator context.allocator = allocator
s: OS_Stat s := _fstat(fd) or_return
s, err = _fstat(fd)
if err != ERROR_NONE {
return fi, err
}
_fill_file_info_from_stat(&fi, s) _fill_file_info_from_stat(&fi, s)
fi.fullpath, err = absolute_path_from_handle(fd) fi.fullpath = absolute_path_from_handle(fd) or_return
if err != ERROR_NONE {
return
}
fi.name = path_base(fi.fullpath) fi.name = path_base(fi.fullpath)
return fi, ERROR_NONE return
} }
+36 -36
View File
@@ -4,7 +4,7 @@ import "core:time"
import "base:runtime" import "base:runtime"
import win32 "core:sys/windows" import win32 "core:sys/windows"
@(private) @(private, require_results)
full_path_from_name :: proc(name: string, allocator := context.allocator) -> (path: string, err: Errno) { full_path_from_name :: proc(name: string, allocator := context.allocator) -> (path: string, err: Errno) {
context.allocator = allocator context.allocator = allocator
@@ -19,10 +19,10 @@ full_path_from_name :: proc(name: string, allocator := context.allocator) -> (pa
for { for {
n := win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil) n := win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil)
if n == 0 { if n == 0 {
return "", Errno(win32.GetLastError()) return "", get_last_error()
} }
if n <= u32(len(buf)) { if n <= u32(len(buf)) {
return win32.utf16_to_utf8(buf[:n], allocator) or_else "", ERROR_NONE return win32.utf16_to_utf8(buf[:n], allocator) or_else "", nil
} }
resize(&buf, len(buf)*2) resize(&buf, len(buf)*2)
} }
@@ -30,7 +30,7 @@ full_path_from_name :: proc(name: string, allocator := context.allocator) -> (pa
return return
} }
@(private) @(private, require_results)
_stat :: proc(name: string, create_file_attributes: u32, allocator := context.allocator) -> (fi: File_Info, e: Errno) { _stat :: proc(name: string, create_file_attributes: u32, allocator := context.allocator) -> (fi: File_Info, e: Errno) {
if len(name) == 0 { if len(name) == 0 {
return {}, ERROR_PATH_NOT_FOUND return {}, ERROR_PATH_NOT_FOUND
@@ -54,7 +54,7 @@ _stat :: proc(name: string, create_file_attributes: u32, allocator := context.al
fd: win32.WIN32_FIND_DATAW fd: win32.WIN32_FIND_DATAW
sh := win32.FindFirstFileW(wname, &fd) sh := win32.FindFirstFileW(wname, &fd)
if sh == win32.INVALID_HANDLE_VALUE { if sh == win32.INVALID_HANDLE_VALUE {
e = Errno(win32.GetLastError()) e = get_last_error()
return return
} }
win32.FindClose(sh) win32.FindClose(sh)
@@ -64,7 +64,7 @@ _stat :: proc(name: string, create_file_attributes: u32, allocator := context.al
h := win32.CreateFileW(wname, 0, 0, nil, win32.OPEN_EXISTING, create_file_attributes, nil) h := win32.CreateFileW(wname, 0, 0, nil, win32.OPEN_EXISTING, create_file_attributes, nil)
if h == win32.INVALID_HANDLE_VALUE { if h == win32.INVALID_HANDLE_VALUE {
e = Errno(win32.GetLastError()) e = get_last_error()
return return
} }
defer win32.CloseHandle(h) defer win32.CloseHandle(h)
@@ -72,26 +72,29 @@ _stat :: proc(name: string, create_file_attributes: u32, allocator := context.al
} }
@(require_results)
lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Errno) { lstat :: proc(name: string, allocator := context.allocator) -> (File_Info, Errno) {
attrs := win32.FILE_FLAG_BACKUP_SEMANTICS attrs := win32.FILE_FLAG_BACKUP_SEMANTICS
attrs |= win32.FILE_FLAG_OPEN_REPARSE_POINT attrs |= win32.FILE_FLAG_OPEN_REPARSE_POINT
return _stat(name, attrs, allocator) return _stat(name, attrs, allocator)
} }
@(require_results)
stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Errno) { stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Errno) {
attrs := win32.FILE_FLAG_BACKUP_SEMANTICS attrs := win32.FILE_FLAG_BACKUP_SEMANTICS
return _stat(name, attrs, allocator) return _stat(name, attrs, allocator)
} }
fstat :: proc(fd: Handle, allocator := context.allocator) -> (fi: File_Info, errno: Errno) { @(require_results)
fstat :: proc(fd: Handle, allocator := context.allocator) -> (fi: File_Info, err: Errno) {
if fd == 0 { if fd == 0 {
return {}, ERROR_INVALID_HANDLE err = ERROR_INVALID_HANDLE
} }
context.allocator = allocator context.allocator = allocator
path, err := cleanpath_from_handle(fd) path := cleanpath_from_handle(fd) or_return
if err != ERROR_NONE { defer if err != nil {
return {}, err delete(path)
} }
h := win32.HANDLE(fd) h := win32.HANDLE(fd)
@@ -99,16 +102,16 @@ fstat :: proc(fd: Handle, allocator := context.allocator) -> (fi: File_Info, err
case win32.FILE_TYPE_PIPE, win32.FILE_TYPE_CHAR: case win32.FILE_TYPE_PIPE, win32.FILE_TYPE_CHAR:
fi.name = basename(path) fi.name = basename(path)
fi.mode |= file_type_mode(h) fi.mode |= file_type_mode(h)
errno = ERROR_NONE err = nil
case: case:
fi, errno = file_info_from_get_file_information_by_handle(path, h) fi = file_info_from_get_file_information_by_handle(path, h) or_return
} }
fi.fullpath = path fi.fullpath = path
return return
} }
@(private) @(private, require_results)
cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 { cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 {
buf := buf buf := buf
N := 0 N := 0
@@ -133,16 +136,13 @@ cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 {
return buf return buf
} }
@(private) @(private, require_results)
cleanpath_from_handle :: proc(fd: Handle) -> (string, Errno) { cleanpath_from_handle :: proc(fd: Handle) -> (s: string, err: Errno) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator) runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
buf, err := cleanpath_from_handle_u16(fd, context.temp_allocator) buf := cleanpath_from_handle_u16(fd, context.temp_allocator) or_return
if err != 0 { return win32.utf16_to_utf8(buf, context.allocator)
return "", err
}
return win32.utf16_to_utf8(buf, context.allocator) or_else "", err
} }
@(private) @(private, require_results)
cleanpath_from_handle_u16 :: proc(fd: Handle, allocator: runtime.Allocator) -> ([]u16, Errno) { cleanpath_from_handle_u16 :: proc(fd: Handle, allocator: runtime.Allocator) -> ([]u16, Errno) {
if fd == 0 { if fd == 0 {
return nil, ERROR_INVALID_HANDLE return nil, ERROR_INVALID_HANDLE
@@ -151,20 +151,20 @@ cleanpath_from_handle_u16 :: proc(fd: Handle, allocator: runtime.Allocator) -> (
n := win32.GetFinalPathNameByHandleW(h, nil, 0, 0) n := win32.GetFinalPathNameByHandleW(h, nil, 0, 0)
if n == 0 { if n == 0 {
return nil, Errno(win32.GetLastError()) return nil, get_last_error()
} }
buf := make([]u16, max(n, win32.DWORD(260))+1, allocator) buf := make([]u16, max(n, win32.DWORD(260))+1, allocator)
buf_len := win32.GetFinalPathNameByHandleW(h, raw_data(buf), n, 0) buf_len := win32.GetFinalPathNameByHandleW(h, raw_data(buf), n, 0)
return buf[:buf_len], ERROR_NONE return buf[:buf_len], nil
} }
@(private) @(private, require_results)
cleanpath_from_buf :: proc(buf: []u16) -> string { cleanpath_from_buf :: proc(buf: []u16) -> string {
buf := buf buf := buf
buf = cleanpath_strip_prefix(buf) buf = cleanpath_strip_prefix(buf)
return win32.utf16_to_utf8(buf, context.allocator) or_else "" return win32.utf16_to_utf8(buf, context.allocator) or_else ""
} }
@(private) @(private, require_results)
basename :: proc(name: string) -> (base: string) { basename :: proc(name: string) -> (base: string) {
name := name name := name
if len(name) > 3 && name[:3] == `\\?` { if len(name) > 3 && name[:3] == `\\?` {
@@ -190,7 +190,7 @@ basename :: proc(name: string) -> (base: string) {
return name return name
} }
@(private) @(private, require_results)
file_type_mode :: proc(h: win32.HANDLE) -> File_Mode { file_type_mode :: proc(h: win32.HANDLE) -> File_Mode {
switch win32.GetFileType(h) { switch win32.GetFileType(h) {
case win32.FILE_TYPE_PIPE: case win32.FILE_TYPE_PIPE:
@@ -202,7 +202,7 @@ file_type_mode :: proc(h: win32.HANDLE) -> File_Mode {
} }
@(private) @(private, require_results)
file_mode_from_file_attributes :: proc(FileAttributes: win32.DWORD, h: win32.HANDLE, ReparseTag: win32.DWORD) -> (mode: 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 { if FileAttributes & win32.FILE_ATTRIBUTE_READONLY != 0 {
mode |= 0o444 mode |= 0o444
@@ -239,7 +239,7 @@ windows_set_file_info_times :: proc(fi: ^File_Info, d: ^$T) {
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime)) fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
} }
@(private) @(private, require_results)
file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string) -> (fi: File_Info, e: Errno) { file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_DATA, name: string) -> (fi: File_Info, e: Errno) {
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow) fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
@@ -254,7 +254,7 @@ file_info_from_win32_file_attribute_data :: proc(d: ^win32.WIN32_FILE_ATTRIBUTE_
return return
} }
@(private) @(private, require_results)
file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string) -> (fi: File_Info, e: Errno) { file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string) -> (fi: File_Info, e: Errno) {
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow) fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
@@ -269,20 +269,20 @@ file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string)
return return
} }
@(private) @(private, require_results)
file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HANDLE) -> (File_Info, Errno) { file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HANDLE) -> (File_Info, Errno) {
d: win32.BY_HANDLE_FILE_INFORMATION d: win32.BY_HANDLE_FILE_INFORMATION
if !win32.GetFileInformationByHandle(h, &d) { if !win32.GetFileInformationByHandle(h, &d) {
err := Errno(win32.GetLastError()) err := get_last_error()
return {}, err return {}, err
} }
ti: win32.FILE_ATTRIBUTE_TAG_INFO ti: win32.FILE_ATTRIBUTE_TAG_INFO
if !win32.GetFileInformationByHandleEx(h, .FileAttributeTagInfo, &ti, size_of(ti)) { if !win32.GetFileInformationByHandleEx(h, .FileAttributeTagInfo, &ti, size_of(ti)) {
err := win32.GetLastError() err := get_last_error()
if err != u32(ERROR_INVALID_PARAMETER) { if err != ERROR_INVALID_PARAMETER {
return {}, Errno(err) return {}, err
} }
// Indicate this is a symlink on FAT file systems // Indicate this is a symlink on FAT file systems
ti.ReparseTag = 0 ti.ReparseTag = 0
@@ -299,5 +299,5 @@ file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HAN
windows_set_file_info_times(&fi, &d) windows_set_file_info_times(&fi, &d)
return fi, ERROR_NONE return fi, nil
} }
+16 -33
View File
@@ -14,44 +14,36 @@ stream_from_handle :: proc(fd: Handle) -> io.Stream {
_file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) { _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
fd := Handle(uintptr(stream_data)) fd := Handle(uintptr(stream_data))
n_int: int n_int: int
os_err: Errno os_err: Error
switch mode { switch mode {
case .Close: case .Close:
close(fd) os_err = close(fd)
case .Flush: case .Flush:
when ODIN_OS == .Windows { os_err = flush(fd)
flush(fd)
} else {
// TOOD(bill): other operating systems
}
case .Read: case .Read:
n_int, os_err = read(fd, p) n_int, os_err = read(fd, p)
n = i64(n_int) n = i64(n_int)
if n == 0 && os_err == 0 { if n == 0 && os_err == nil {
err = .EOF err = .EOF
} }
case .Read_At: case .Read_At:
when !(ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Haiku) { n_int, os_err = read_at(fd, p, offset)
n_int, os_err = read_at(fd, p, offset) n = i64(n_int)
n = i64(n_int) if n == 0 && os_err == nil {
if n == 0 && os_err == 0 { err = .EOF
err = .EOF
}
} }
case .Write: case .Write:
n_int, os_err = write(fd, p) n_int, os_err = write(fd, p)
n = i64(n_int) n = i64(n_int)
if n == 0 && os_err == 0 { if n == 0 && os_err == nil {
err = .EOF err = .EOF
} }
case .Write_At: case .Write_At:
when !(ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Haiku) { n_int, os_err = write_at(fd, p, offset)
n_int, os_err = write_at(fd, p, offset) n = i64(n_int)
n = i64(n_int) if n == 0 && os_err == nil {
if n == 0 && os_err == 0 { err = .EOF
err = .EOF
}
} }
case .Seek: case .Seek:
n, os_err = seek(fd, offset, int(whence)) n, os_err = seek(fd, offset, int(whence))
@@ -60,20 +52,11 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte,
case .Destroy: case .Destroy:
err = .Empty err = .Empty
case .Query: case .Query:
when ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Haiku { return io.query_utility({.Close, .Flush, .Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Query})
return io.query_utility({.Close, .Flush, .Read, .Write, .Seek, .Size, .Query})
} else {
return io.query_utility({.Close, .Flush, .Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Query})
}
} }
if err == nil && os_err != 0 { if err == nil && os_err != nil {
when ODIN_OS == .Windows { err = error_to_io_error(os_err)
if os_err == ERROR_HANDLE_EOF {
return n, .EOF
}
}
err = .Unknown
} }
return return
} }
+2 -2
View File
@@ -271,7 +271,7 @@ _glob :: proc(dir, pattern: string, matches: ^[dynamic]string, allocator := cont
d, derr := os.open(dir, os.O_RDONLY) d, derr := os.open(dir, os.O_RDONLY)
if derr != 0 { if derr != nil {
return return
} }
defer os.close(d) defer os.close(d)
@@ -280,7 +280,7 @@ _glob :: proc(dir, pattern: string, matches: ^[dynamic]string, allocator := cont
file_info, ferr := os.fstat(d) file_info, ferr := os.fstat(d)
defer os.file_info_delete(file_info) defer os.file_info_delete(file_info)
if ferr != 0 { if ferr != nil {
return return
} }
if !file_info.is_dir { if !file_info.is_dir {
+5 -5
View File
@@ -52,7 +52,7 @@ is_abs :: proc(path: string) -> bool {
@(private) @(private)
temp_full_path :: proc(name: string) -> (path: string, err: os.Errno) { temp_full_path :: proc(name: string) -> (path: string, err: os.Error) {
ta := context.temp_allocator ta := context.temp_allocator
name := name name := name
@@ -63,17 +63,17 @@ temp_full_path :: proc(name: string) -> (path: string, err: os.Errno) {
p := win32.utf8_to_utf16(name, ta) p := win32.utf8_to_utf16(name, ta)
n := win32.GetFullPathNameW(raw_data(p), 0, nil, nil) n := win32.GetFullPathNameW(raw_data(p), 0, nil, nil)
if n == 0 { if n == 0 {
return "", os.Errno(win32.GetLastError()) return "", os.get_last_error()
} }
buf := make([]u16, n, ta) buf := make([]u16, n, ta)
n = win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil) n = win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil)
if n == 0 { if n == 0 {
delete(buf) delete(buf)
return "", os.Errno(win32.GetLastError()) return "", os.get_last_error()
} }
return win32.utf16_to_utf8(buf[:n], ta) or_else "", os.ERROR_NONE return win32.utf16_to_utf8(buf[:n], ta)
} }
@@ -81,7 +81,7 @@ temp_full_path :: proc(name: string) -> (path: string, err: os.Errno) {
abs :: proc(path: string, allocator := context.allocator) -> (string, bool) { abs :: proc(path: string, allocator := context.allocator) -> (string, bool) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = allocator == context.temp_allocator) runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = allocator == context.temp_allocator)
full_path, err := temp_full_path(path) full_path, err := temp_full_path(path)
if err != 0 { if err != nil {
return "", false return "", false
} }
p := clean(full_path, allocator) p := clean(full_path, allocator)
+14 -21
View File
@@ -14,7 +14,7 @@ import "core:slice"
// The sole exception is if 'skip_dir' is returned as true: // The sole exception is if 'skip_dir' is returned as true:
// when 'skip_dir' is invoked on a directory. 'walk' skips directory contents // when 'skip_dir' is invoked on a directory. 'walk' skips directory contents
// when 'skip_dir' is invoked on a non-directory. 'walk' skips the remaining files in the containing directory // when 'skip_dir' is invoked on a non-directory. 'walk' skips the remaining files in the containing directory
Walk_Proc :: #type proc(info: os.File_Info, in_err: os.Errno, user_data: rawptr) -> (err: os.Errno, skip_dir: bool) Walk_Proc :: #type proc(info: os.File_Info, in_err: os.Error, user_data: rawptr) -> (err: os.Error, skip_dir: bool)
// walk walks the file tree rooted at 'root', calling 'walk_proc' for each file or directory in the tree, including 'root' // walk walks the file tree rooted at 'root', calling 'walk_proc' for each file or directory in the tree, including 'root'
// All errors that happen visiting files and directories are filtered by walk_proc // All errors that happen visiting files and directories are filtered by walk_proc
@@ -22,44 +22,44 @@ Walk_Proc :: #type proc(info: os.File_Info, in_err: os.Errno, user_data: rawptr)
// NOTE: Walking large directories can be inefficient due to the lexical sort // NOTE: Walking large directories can be inefficient due to the lexical sort
// NOTE: walk does not follow symbolic links // NOTE: walk does not follow symbolic links
// NOTE: os.File_Info uses the 'context.temp_allocator' to allocate, and will delete when it is done // NOTE: os.File_Info uses the 'context.temp_allocator' to allocate, and will delete when it is done
walk :: proc(root: string, walk_proc: Walk_Proc, user_data: rawptr) -> os.Errno { walk :: proc(root: string, walk_proc: Walk_Proc, user_data: rawptr) -> os.Error {
info, err := os.lstat(root, context.temp_allocator) info, err := os.lstat(root, context.temp_allocator)
defer os.file_info_delete(info, context.temp_allocator) defer os.file_info_delete(info, context.temp_allocator)
skip_dir: bool skip_dir: bool
if err != 0 { if err != nil {
err, skip_dir = walk_proc(info, err, user_data) err, skip_dir = walk_proc(info, err, user_data)
} else { } else {
err, skip_dir = _walk(info, walk_proc, user_data) err, skip_dir = _walk(info, walk_proc, user_data)
} }
return 0 if skip_dir else err return nil if skip_dir else err
} }
@(private) @(private)
_walk :: proc(info: os.File_Info, walk_proc: Walk_Proc, user_data: rawptr) -> (err: os.Errno, skip_dir: bool) { _walk :: proc(info: os.File_Info, walk_proc: Walk_Proc, user_data: rawptr) -> (err: os.Error, skip_dir: bool) {
if !info.is_dir { if !info.is_dir {
if info.fullpath == "" && info.name == "" { if info.fullpath == "" && info.name == "" {
// ignore empty things // ignore empty things
return return
} }
return walk_proc(info, 0, user_data) return walk_proc(info, nil, user_data)
} }
fis: []os.File_Info fis: []os.File_Info
err1: os.Errno err1: os.Error
fis, err = read_dir(info.fullpath, context.temp_allocator) fis, err = read_dir(info.fullpath, context.temp_allocator)
defer os.file_info_slice_delete(fis, context.temp_allocator) defer os.file_info_slice_delete(fis, context.temp_allocator)
err1, skip_dir = walk_proc(info, err, user_data) err1, skip_dir = walk_proc(info, err, user_data)
if err != 0 || err1 != 0 || skip_dir { if err != nil || err1 != nil || skip_dir {
err = err1 err = err1
return return
} }
for fi in fis { for fi in fis {
err, skip_dir = _walk(fi, walk_proc, user_data) err, skip_dir = _walk(fi, walk_proc, user_data)
if err != 0 || skip_dir { if err != nil || skip_dir {
if !fi.is_dir || !skip_dir { if !fi.is_dir || !skip_dir {
return return
} }
@@ -70,19 +70,12 @@ _walk :: proc(info: os.File_Info, walk_proc: Walk_Proc, user_data: rawptr) -> (e
} }
@(private) @(private)
read_dir :: proc(dir_name: string, allocator := context.temp_allocator) -> ([]os.File_Info, os.Errno) { read_dir :: proc(dir_name: string, allocator := context.temp_allocator) -> (fis: []os.File_Info, err: os.Error) {
f, err := os.open(dir_name, os.O_RDONLY) f := os.open(dir_name, os.O_RDONLY) or_return
if err != 0 { defer os.close(f)
return nil, err fis = os.read_dir(f, -1, allocator) or_return
}
fis: []os.File_Info
fis, err = os.read_dir(f, -1, allocator)
os.close(f)
if err != 0 {
return nil, err
}
slice.sort_by(fis, proc(a, b: os.File_Info) -> bool { slice.sort_by(fis, proc(a, b: os.File_Info) -> bool {
return a.name < b.name return a.name < b.name
}) })
return fis, 0 return
} }
+2 -2
View File
@@ -68,7 +68,7 @@ BUFFER_DEFAULT_SIZE :: 0x10_0000
context_create_with_scale :: proc(filename: string, precise_time: bool, timestamp_scale: f64) -> (ctx: Context, ok: bool) #optional_ok { context_create_with_scale :: proc(filename: string, precise_time: bool, timestamp_scale: f64) -> (ctx: Context, ok: bool) #optional_ok {
fd, err := os.open(filename, os.O_WRONLY | os.O_APPEND | os.O_CREATE | os.O_TRUNC, 0o600) fd, err := os.open(filename, os.O_WRONLY | os.O_APPEND | os.O_CREATE | os.O_TRUNC, 0o600)
if err != os.ERROR_NONE { if err != nil {
return return
} }
@@ -227,7 +227,7 @@ _buffer_end :: proc "contextless" (ctx: ^Context, buffer: ^Buffer) #no_bounds_ch
} }
@(no_instrumentation) @(no_instrumentation)
write :: proc "contextless" (fd: os.Handle, buf: []byte) -> (n: int, err: os.Errno) { write :: proc "contextless" (fd: os.Handle, buf: []byte) -> (n: int, err: os.Error) {
return _write(fd, buf) return _write(fd, buf)
} }
+3 -12
View File
@@ -10,21 +10,12 @@ import "core:sys/linux"
MAX_RW :: 0x7fffffff MAX_RW :: 0x7fffffff
@(no_instrumentation) @(no_instrumentation)
_write :: proc "contextless" (fd: os.Handle, data: []byte) -> (n: int, err: os.Errno) #no_bounds_check /* bounds check would segfault instrumentation */ { _write :: proc "contextless" (fd: os.Handle, data: []byte) -> (n: int, err: os.Error) #no_bounds_check /* bounds check would segfault instrumentation */ {
if len(data) == 0 {
return 0, os.ERROR_NONE
}
for n < len(data) { for n < len(data) {
chunk := data[:min(len(data), MAX_RW)] chunk := data[:min(len(data), MAX_RW)]
written, errno := linux.write(linux.Fd(fd), chunk) n += linux.write(linux.Fd(fd), chunk) or_return
if errno != .NONE {
return n, os.Errno(errno)
}
n += written
} }
return
return n, os.ERROR_NONE
} }
CLOCK_MONOTONIC_RAW :: 4 // NOTE(tetra): "RAW" means: Not adjusted by NTP. CLOCK_MONOTONIC_RAW :: 4 // NOTE(tetra): "RAW" means: Not adjusted by NTP.
+4 -9
View File
@@ -22,29 +22,24 @@ foreign libc {
@(link_name="clock_gettime") _unix_clock_gettime :: proc(clock_id: u64, timespec: ^timespec) -> i32 --- @(link_name="clock_gettime") _unix_clock_gettime :: proc(clock_id: u64, timespec: ^timespec) -> i32 ---
} }
@(no_instrumentation)
get_last_error :: proc "contextless" () -> int {
return int(__error()^)
}
MAX_RW :: 0x7fffffff MAX_RW :: 0x7fffffff
@(no_instrumentation) @(no_instrumentation)
_write :: proc "contextless" (fd: os.Handle, data: []byte) -> (n: int, err: os.Errno) #no_bounds_check /* bounds check would segfault instrumentation */ { _write :: proc "contextless" (fd: os.Handle, data: []byte) -> (n: int, err: os.Error) #no_bounds_check /* bounds check would segfault instrumentation */ {
if len(data) == 0 { if len(data) == 0 {
return 0, os.ERROR_NONE return 0, nil
} }
for n < len(data) { for n < len(data) {
chunk := data[:min(len(data), MAX_RW)] chunk := data[:min(len(data), MAX_RW)]
written := _unix_write(fd, raw_data(chunk), len(chunk)) written := _unix_write(fd, raw_data(chunk), len(chunk))
if written < 0 { if written < 0 {
return n, os.Errno(get_last_error()) return n, os.get_last_error()
} }
n += written n += written
} }
return n, os.ERROR_NONE return n, nil
} }
CLOCK_MONOTONIC_RAW :: 4 // NOTE(tetra): "RAW" means: Not adjusted by NTP. CLOCK_MONOTONIC_RAW :: 4 // NOTE(tetra): "RAW" means: Not adjusted by NTP.
+4 -5
View File
@@ -10,9 +10,9 @@ import win32 "core:sys/windows"
MAX_RW :: 1<<30 MAX_RW :: 1<<30
@(no_instrumentation) @(no_instrumentation)
_write :: proc "contextless" (fd: os.Handle, data: []byte) -> (int, os.Errno) #no_bounds_check /* bounds check would segfault instrumentation */ { _write :: proc "contextless" (fd: os.Handle, data: []byte) -> (int, os.Error) #no_bounds_check /* bounds check would segfault instrumentation */ {
if len(data) == 0 { if len(data) == 0 {
return 0, os.ERROR_NONE return 0, nil
} }
single_write_length: win32.DWORD single_write_length: win32.DWORD
@@ -25,12 +25,11 @@ _write :: proc "contextless" (fd: os.Handle, data: []byte) -> (int, os.Errno) #n
e := win32.WriteFile(win32.HANDLE(fd), &data[total_write], to_write, &single_write_length, nil) e := win32.WriteFile(win32.HANDLE(fd), &data[total_write], to_write, &single_write_length, nil)
if single_write_length <= 0 || !e { if single_write_length <= 0 || !e {
err := os.Errno(win32.GetLastError()) return int(total_write), os.get_last_error()
return int(total_write), err
} }
total_write += i64(single_write_length) total_write += i64(single_write_length)
} }
return int(total_write), os.ERROR_NONE return int(total_write), nil
} }
@(no_instrumentation) @(no_instrumentation)
+5 -6
View File
@@ -2,7 +2,6 @@
package sync package sync
import "core:c" import "core:c"
import "base:runtime"
import "core:sys/haiku" import "core:sys/haiku"
import "core:sys/unix" import "core:sys/unix"
import "core:time" import "core:time"
@@ -86,10 +85,10 @@ _futex_wait :: proc "contextless" (f: ^Futex, expect: u32) -> (ok: bool) {
waiter.prev.next = waiter.next waiter.prev.next = waiter.next
waiter.next.prev = waiter.prev waiter.next.prev = waiter.prev
unix.pthread_sigmask(haiku.SIG_SETMASK, &old_mask, nil) _ = unix.pthread_sigmask(haiku.SIG_SETMASK, &old_mask, nil)
// FIXME: Add error handling! // FIXME: Add error handling!
return return
} }
_futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expect: u32, duration: time.Duration) -> (ok: bool) { _futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expect: u32, duration: time.Duration) -> (ok: bool) {
@@ -133,10 +132,10 @@ _futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expect: u32, duration
waiter.prev.next = waiter.next waiter.prev.next = waiter.next
waiter.next.prev = waiter.prev waiter.next.prev = waiter.prev
unix.pthread_sigmask(haiku.SIG_SETMASK, &old_mask, nil) unix.pthread_sigmask(haiku.SIG_SETMASK, &old_mask, nil)
// FIXME: Add error handling! // FIXME: Add error handling!
return return
} }
_futex_signal :: proc "contextless" (f: ^Futex) { _futex_signal :: proc "contextless" (f: ^Futex) {
+1 -1
View File
@@ -399,7 +399,7 @@ cpu_topology_node_info :: struct {
}, },
_package: struct { _package: struct {
vendor: cpu_vendor, vendor: cpu_vendor,
cache_line_size: u32 cache_line_size: u32,
}, },
_core: struct { _core: struct {
model: u32, model: u32,
+1
View File
@@ -16,6 +16,7 @@ CLOCK_REALTIME :: clockid_t(2)
CLOCK_THREAD_CPUTIME_ID :: clockid_t(3) CLOCK_THREAD_CPUTIME_ID :: clockid_t(3)
errno_t :: enum u16 { errno_t :: enum u16 {
NONE = 0,
// No error occurred. System call completed successfully. // No error occurred. System call completed successfully.
SUCCESS = 0, SUCCESS = 0,
// Argument list too long. // Argument list too long.
+2 -2
View File
@@ -868,8 +868,8 @@ To partly mitigate this, redirect STDERR to a file or use the -define:ODIN_TEST_
when ODIN_OS != .Windows { when ODIN_OS != .Windows {
mode = os.S_IRUSR|os.S_IWUSR|os.S_IRGRP|os.S_IROTH mode = os.S_IRUSR|os.S_IWUSR|os.S_IRGRP|os.S_IROTH
} }
json_fd, errno := os.open(JSON_REPORT, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode) json_fd, err := os.open(JSON_REPORT, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode)
fmt.assertf(errno == os.ERROR_NONE, "unable to open file %q for writing of JSON report, error: %v", JSON_REPORT, errno) fmt.assertf(err == nil, "unable to open file %q for writing of JSON report, error: %v", JSON_REPORT, err)
defer os.close(json_fd) defer os.close(json_fd)
for test, i in report.all_tests { for test, i in report.all_tests {
+1 -1
View File
@@ -359,7 +359,7 @@ control_flow :: proc() {
if false { if false {
f, err := os.open("my_file.txt") f, err := os.open("my_file.txt")
if err != os.ERROR_NONE { if err != nil {
// handle error // handle error
} }
defer os.close(f) defer os.close(f)
+23
View File
@@ -127,6 +127,8 @@ gb_internal bool complete_soa_type(Checker *checker, Type *t, bool wait_to_finis
gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y); gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y);
gb_internal bool is_exact_value_zero(ExactValue const &v);
enum LoadDirectiveResult { enum LoadDirectiveResult {
LoadDirective_Success = 0, LoadDirective_Success = 0,
LoadDirective_Error = 1, LoadDirective_Error = 1,
@@ -4457,6 +4459,27 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
case Type_Union: case Type_Union:
// IMPORTANT NOTE HACK(bill): This is just to allow for comparisons against `0` with the `os.Error` type
// as a kind of transition period
if (!build_context.strict_style &&
operand->mode == Addressing_Constant &&
target_type->kind == Type_Named &&
(c->pkg == nullptr || c->pkg->name != "os") &&
target_type->Named.name == "Error") {
Entity *e = target_type->Named.type_name;
if (e->pkg && e->pkg->name == "os") {
if (is_exact_value_zero(operand->value) &&
(operand->value.kind == ExactValue_Integer ||
operand->value.kind == ExactValue_Float)) {
operand->mode = Addressing_Value;
target_type = t_untyped_nil;
operand->value = empty_exact_value;
update_untyped_expr_value(c, operand->expr, operand->value);
break;
}
}
}
// "fallthrough"
if (!is_operand_nil(*operand) && !is_operand_uninit(*operand)) { if (!is_operand_nil(*operand) && !is_operand_uninit(*operand)) {
TEMPORARY_ALLOCATOR_GUARD(); TEMPORARY_ALLOCATOR_GUARD();
+1 -1
View File
@@ -1117,7 +1117,7 @@ test_os_handle :: proc(t: ^testing.T) {
// Delete the file now that we're done. // Delete the file now that we're done.
// //
// This is not done all the time, just in case the file is useful to debugging. // This is not done all the time, just in case the file is useful to debugging.
testing.expect_value(t, os.remove(TEMPORARY_FILENAME), os.ERROR_NONE) testing.expect_value(t, os.remove(TEMPORARY_FILENAME), nil)
} }
TEMPORARY_FILENAME :: "test_core_flags_write_test_output_data" TEMPORARY_FILENAME :: "test_core_flags_write_test_output_data"
+4 -4
View File
@@ -7,12 +7,12 @@ import "core:testing"
@(test) @(test)
read_dir :: proc(t: ^testing.T) { read_dir :: proc(t: ^testing.T) {
fd, errno := os.open(#directory + "/dir") fd, err := os.open(#directory + "/dir")
testing.expect_value(t, errno, os.ERROR_NONE) testing.expect_value(t, err, nil)
defer os.close(fd) defer os.close(fd)
dir, errno2 := os.read_dir(fd, -1) dir, err2 := os.read_dir(fd, -1)
testing.expect_value(t, errno2, os.ERROR_NONE) testing.expect_value(t, err2, nil)
defer os.file_info_slice_delete(dir) defer os.file_info_slice_delete(dir)
slice.sort_by_key(dir, proc(fi: os.File_Info) -> string { return fi.name }) slice.sort_by_key(dir, proc(fi: os.File_Info) -> string { return fi.name })
@@ -439,7 +439,7 @@ main :: proc() {
} }
save_path := fmt.tprintf("verify/test_%v_%v.odin", test.package_name, code_test_name) save_path := fmt.tprintf("verify/test_%v_%v.odin", test.package_name, code_test_name)
test_file_handle, err := os.open(save_path, os.O_WRONLY | os.O_CREATE); if err != 0 { test_file_handle, err := os.open(save_path, os.O_WRONLY | os.O_CREATE); if err != nil {
fmt.eprintf("We could not open the file to the path %q for writing\n", save_path) fmt.eprintf("We could not open the file to the path %q for writing\n", save_path)
g_bad_doc = true g_bad_doc = true
continue continue