mirror of
https://github.com/odin-lang/Odin.git
synced 2025-12-30 18:02:02 +00:00
Merge pull request #4020 from odin-lang/bill/os-errno
`os.Error` to replace `os.Errno`
This commit is contained in:
@@ -8,9 +8,9 @@ HAS_RAND_BYTES :: true
|
||||
|
||||
@(private)
|
||||
_rand_bytes :: proc(dst: []byte) {
|
||||
ret := (os.Errno)(win32.BCryptGenRandom(nil, raw_data(dst), u32(len(dst)), win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG))
|
||||
if ret != os.ERROR_NONE {
|
||||
switch ret {
|
||||
ret := os.Platform_Error(win32.BCryptGenRandom(nil, raw_data(dst), u32(len(dst)), win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG))
|
||||
if ret != nil {
|
||||
#partial switch ret {
|
||||
case os.ERROR_INVALID_HANDLE:
|
||||
// The handle to the first parameter is invalid.
|
||||
// This should not happen here, since we explicitly pass nil to it
|
||||
|
||||
@@ -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
|
||||
defer csv.reader_destroy(&r)
|
||||
|
||||
handle, errno := os.open(filename)
|
||||
if errno != os.ERROR_NONE {
|
||||
fmt.printfln("Error opening file: %v", filename)
|
||||
handle, err := os.open(filename)
|
||||
if err != nil {
|
||||
fmt.eprintfln("Error opening file: %v", filename)
|
||||
return
|
||||
}
|
||||
defer os.close(handle)
|
||||
|
||||
@@ -28,7 +28,7 @@ Parse_Error :: struct {
|
||||
// Provides more granular information than what just a string could hold.
|
||||
Open_File_Error :: struct {
|
||||
filename: string,
|
||||
errno: os.Errno,
|
||||
errno: os.Error,
|
||||
mode: int,
|
||||
perms: int,
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
if errno != 0 {
|
||||
// NOTE(Feoramund): os.Errno is system-dependent, and there's
|
||||
if errno != nil {
|
||||
// NOTE(Feoramund): os.Error is system-dependent, and there's
|
||||
// currently no good way to translate them all into strings.
|
||||
//
|
||||
// The upcoming `os2` package will hopefully solve this.
|
||||
|
||||
@@ -27,7 +27,7 @@ which :: proc{
|
||||
|
||||
which_file :: proc(path: string) -> Which_File_Type {
|
||||
f, err := os.open(path)
|
||||
if err != 0 {
|
||||
if err != nil {
|
||||
return .Unknown
|
||||
}
|
||||
header: [128]byte
|
||||
|
||||
@@ -213,7 +213,7 @@ write_image_as_ppm :: proc(filename: string, image: ^image.Image) -> (success: b
|
||||
}
|
||||
|
||||
fd, err := open(filename, flags, mode)
|
||||
if err != 0 {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
defer close(fd)
|
||||
|
||||
@@ -450,7 +450,7 @@ when false {
|
||||
}
|
||||
|
||||
fd, fderr := open(filename, flags, mode)
|
||||
if fderr != 0 {
|
||||
if fderr != nil {
|
||||
return .Cannot_Open_File
|
||||
}
|
||||
defer close(fd)
|
||||
|
||||
@@ -24,7 +24,7 @@ map_file :: proc{
|
||||
|
||||
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)
|
||||
if err != 0 {
|
||||
if err != nil {
|
||||
return nil, .Open_Failure
|
||||
}
|
||||
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) {
|
||||
size, os_err := os.file_size(os.Handle(fd))
|
||||
if os_err != 0 {
|
||||
if os_err != nil {
|
||||
return nil, .Stat_Failure
|
||||
}
|
||||
if size < 0 {
|
||||
|
||||
@@ -53,9 +53,9 @@ _create_socket :: proc(family: Address_Family, protocol: Socket_Protocol) -> (so
|
||||
unreachable()
|
||||
}
|
||||
|
||||
sock, ok := os.socket(c_family, c_type, c_protocol)
|
||||
if ok != os.ERROR_NONE {
|
||||
err = Create_Socket_Error(ok)
|
||||
sock, sock_err := os.socket(c_family, c_type, c_protocol)
|
||||
if sock_err != nil {
|
||||
err = Create_Socket_Error(os.is_platform_error(sock_err) or_else -1)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -84,8 +84,8 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
|
||||
|
||||
sockaddr := _endpoint_to_sockaddr(endpoint)
|
||||
res := os.connect(os.Socket(skt), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len))
|
||||
if res != os.ERROR_NONE {
|
||||
err = Dial_Error(res)
|
||||
if res != nil {
|
||||
err = Dial_Error(os.is_platform_error(res) or_else -1)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -100,11 +100,11 @@ _bind :: proc(skt: Any_Socket, ep: Endpoint) -> (err: Network_Error) {
|
||||
sockaddr := _endpoint_to_sockaddr(ep)
|
||||
s := any_socket_to_socket(skt)
|
||||
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 {
|
||||
err = .Privileged_Port_Without_Root
|
||||
} else {
|
||||
err = Bind_Error(res)
|
||||
err = Bind_Error(os.is_platform_error(res) or_else -1)
|
||||
}
|
||||
}
|
||||
return
|
||||
@@ -128,8 +128,8 @@ _listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (skt: TCP_
|
||||
bind(sock, interface_endpoint) or_return
|
||||
|
||||
res := os.listen(os.Socket(skt), backlog)
|
||||
if res != os.ERROR_NONE {
|
||||
err = Listen_Error(res)
|
||||
if res != nil {
|
||||
err = Listen_Error(os.is_platform_error(res) or_else -1)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -141,9 +141,9 @@ _accept_tcp :: proc(sock: TCP_Socket, options := default_tcp_options) -> (client
|
||||
sockaddr: os.SOCKADDR_STORAGE_LH
|
||||
sockaddrlen := c.int(size_of(sockaddr))
|
||||
|
||||
client_sock, ok := os.accept(os.Socket(sock), cast(^os.SOCKADDR) &sockaddr, &sockaddrlen)
|
||||
if ok != os.ERROR_NONE {
|
||||
err = Accept_Error(ok)
|
||||
client_sock, client_sock_err := os.accept(os.Socket(sock), cast(^os.SOCKADDR) &sockaddr, &sockaddrlen)
|
||||
if client_sock_err != nil {
|
||||
err = Accept_Error(os.is_platform_error(client_sock_err) or_else -1)
|
||||
return
|
||||
}
|
||||
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 {
|
||||
return
|
||||
}
|
||||
res, ok := os.recv(os.Socket(skt), buf, 0)
|
||||
if ok != os.ERROR_NONE {
|
||||
err = TCP_Recv_Error(ok)
|
||||
res, res_err := os.recv(os.Socket(skt), buf, 0)
|
||||
if res_err != nil {
|
||||
err = TCP_Recv_Error(os.is_platform_error(res_err) or_else -1)
|
||||
return
|
||||
}
|
||||
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
|
||||
fromsize := c.int(size_of(from))
|
||||
res, ok := os.recvfrom(os.Socket(skt), buf, 0, cast(^os.SOCKADDR) &from, &fromsize)
|
||||
if ok != os.ERROR_NONE {
|
||||
err = UDP_Recv_Error(ok)
|
||||
res, res_err := os.recvfrom(os.Socket(skt), buf, 0, cast(^os.SOCKADDR) &from, &fromsize)
|
||||
if res_err != nil {
|
||||
err = UDP_Recv_Error(os.is_platform_error(res_err) or_else -1)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -194,9 +194,9 @@ _send_tcp :: proc(skt: TCP_Socket, buf: []byte) -> (bytes_written: int, err: Net
|
||||
for bytes_written < len(buf) {
|
||||
limit := min(int(max(i32)), len(buf) - bytes_written)
|
||||
remaining := buf[bytes_written:][:limit]
|
||||
res, ok := os.send(os.Socket(skt), remaining, 0)
|
||||
if ok != os.ERROR_NONE {
|
||||
err = TCP_Send_Error(ok)
|
||||
res, res_err := os.send(os.Socket(skt), remaining, 0)
|
||||
if res_err != nil {
|
||||
err = TCP_Send_Error(os.is_platform_error(res_err) or_else -1)
|
||||
return
|
||||
}
|
||||
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) {
|
||||
limit := min(1<<31, len(buf) - bytes_written)
|
||||
remaining := buf[bytes_written:][:limit]
|
||||
res, ok := os.sendto(os.Socket(skt), remaining, 0, cast(^os.SOCKADDR)&toaddr, i32(toaddr.len))
|
||||
if ok != os.ERROR_NONE {
|
||||
err = UDP_Send_Error(ok)
|
||||
res, res_err := os.sendto(os.Socket(skt), remaining, 0, cast(^os.SOCKADDR)&toaddr, i32(toaddr.len))
|
||||
if res_err != nil {
|
||||
err = UDP_Send_Error(os.is_platform_error(res_err) or_else -1)
|
||||
return
|
||||
}
|
||||
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) {
|
||||
s := any_socket_to_socket(skt)
|
||||
res := os.shutdown(os.Socket(s), int(manner))
|
||||
if res != os.ERROR_NONE {
|
||||
return Shutdown_Error(res)
|
||||
if res != nil {
|
||||
return Shutdown_Error(os.is_platform_error(res) or_else -1)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -302,8 +302,8 @@ _set_option :: proc(s: Any_Socket, option: Socket_Option, value: any, loc := #ca
|
||||
|
||||
skt := any_socket_to_socket(s)
|
||||
res := os.setsockopt(os.Socket(skt), int(level), int(option), ptr, len)
|
||||
if res != os.ERROR_NONE {
|
||||
return Socket_Option_Error(res)
|
||||
if res != nil {
|
||||
return Socket_Option_Error(os.is_platform_error(res) or_else -1)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -314,8 +314,8 @@ _set_blocking :: proc(socket: Any_Socket, should_block: bool) -> (err: Network_E
|
||||
socket := any_socket_to_socket(socket)
|
||||
|
||||
flags, getfl_err := os.fcntl(int(socket), os.F_GETFL, 0)
|
||||
if getfl_err != os.ERROR_NONE {
|
||||
return Set_Blocking_Error(getfl_err)
|
||||
if getfl_err != nil {
|
||||
return Set_Blocking_Error(os.is_platform_error(getfl_err) or_else -1)
|
||||
}
|
||||
|
||||
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)
|
||||
if setfl_err != os.ERROR_NONE {
|
||||
return Set_Blocking_Error(setfl_err)
|
||||
if setfl_err != nil {
|
||||
return Set_Blocking_Error(os.is_platform_error(setfl_err) or_else -1)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -3,21 +3,12 @@ package os
|
||||
|
||||
import "core:strings"
|
||||
|
||||
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Errno) {
|
||||
dirp: Dir
|
||||
dirp, err = _fdopendir(fd)
|
||||
if err != ERROR_NONE {
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []File_Info, err: Error) {
|
||||
dirp := _fdopendir(fd) or_return
|
||||
defer _closedir(dirp)
|
||||
|
||||
dirpath: string
|
||||
dirpath, err = absolute_path_from_handle(fd)
|
||||
if err != ERROR_NONE {
|
||||
return
|
||||
}
|
||||
|
||||
dirpath := absolute_path_from_handle(fd) or_return
|
||||
defer delete(dirpath)
|
||||
|
||||
n := n
|
||||
@@ -27,8 +18,8 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
|
||||
size = 100
|
||||
}
|
||||
|
||||
dfi := make([dynamic]File_Info, 0, size, allocator)
|
||||
defer if err != ERROR_NONE {
|
||||
dfi := make([dynamic]File_Info, 0, size, allocator) or_return
|
||||
defer if err != nil {
|
||||
for fi_ in dfi {
|
||||
file_info_delete(fi_, allocator)
|
||||
}
|
||||
@@ -39,7 +30,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
|
||||
entry: Dirent
|
||||
end_of_stream: bool
|
||||
entry, err, end_of_stream = _readdir(dirp)
|
||||
if err != ERROR_NONE {
|
||||
if err != nil {
|
||||
return
|
||||
} else if end_of_stream {
|
||||
break
|
||||
@@ -56,7 +47,7 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
|
||||
|
||||
s: OS_Stat
|
||||
s, err = _lstat(fullpath)
|
||||
if err != ERROR_NONE {
|
||||
if err != nil {
|
||||
delete(fullpath, allocator)
|
||||
return
|
||||
}
|
||||
@@ -67,5 +58,5 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
|
||||
append(&dfi, fi_)
|
||||
}
|
||||
|
||||
return dfi[:], ERROR_NONE
|
||||
return dfi[:], nil
|
||||
}
|
||||
|
||||
@@ -4,7 +4,9 @@ import win32 "core:sys/windows"
|
||||
import "core:strings"
|
||||
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) {
|
||||
// Ignore "." and ".."
|
||||
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)
|
||||
if !dir_fi.is_dir {
|
||||
return nil, ERROR_FILE_IS_NOT_DIR
|
||||
return nil, .Not_Dir
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
wpath: []u16
|
||||
wpath, err = cleanpath_from_handle_u16(fd, context.temp_allocator)
|
||||
if len(wpath) == 0 || err != ERROR_NONE {
|
||||
wpath := cleanpath_from_handle_u16(fd, context.temp_allocator) or_return
|
||||
if len(wpath) == 0 {
|
||||
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)
|
||||
wpath_search[len(wpath)+0] = '\\'
|
||||
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_handle := win32.FindFirstFileW(raw_data(wpath_search), find_data)
|
||||
if find_handle == win32.INVALID_HANDLE_VALUE {
|
||||
err = Errno(win32.GetLastError())
|
||||
err = get_last_error()
|
||||
return dfi[:], err
|
||||
}
|
||||
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) {
|
||||
e := Errno(win32.GetLastError())
|
||||
e := get_last_error()
|
||||
if e == ERROR_NO_MORE_FILES {
|
||||
break
|
||||
}
|
||||
@@ -109,5 +110,5 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
|
||||
}
|
||||
}
|
||||
|
||||
return dfi[:], ERROR_NONE
|
||||
return dfi[:], nil
|
||||
}
|
||||
|
||||
@@ -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
|
||||
// Otherwise the returned value will be empty and the boolean will be false
|
||||
// NOTE: the value will be allocated with the supplied allocator
|
||||
@(require_results)
|
||||
lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) {
|
||||
if key == "" {
|
||||
return
|
||||
}
|
||||
wkey := win32.utf8_to_wstring(key)
|
||||
n := win32.GetEnvironmentVariableW(wkey, nil, 0)
|
||||
if n == 0 {
|
||||
err := win32.GetLastError()
|
||||
if err == u32(ERROR_ENVVAR_NOT_FOUND) {
|
||||
return "", false
|
||||
}
|
||||
if n == 0 && get_last_error() == ERROR_ENVVAR_NOT_FOUND {
|
||||
return "", false
|
||||
}
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
|
||||
|
||||
b := make([dynamic]u16, n, context.temp_allocator)
|
||||
b, _ := make([dynamic]u16, n, context.temp_allocator)
|
||||
n = win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b)))
|
||||
if n == 0 {
|
||||
err := win32.GetLastError()
|
||||
if err == u32(ERROR_ENVVAR_NOT_FOUND) {
|
||||
return "", false
|
||||
}
|
||||
if n == 0 && get_last_error() == ERROR_ENVVAR_NOT_FOUND {
|
||||
return "", false
|
||||
}
|
||||
value, _ = win32.utf16_to_utf8(b[:n], allocator)
|
||||
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
|
||||
// To distinguish between an empty value and an unset value, use lookup_env
|
||||
// NOTE: the value will be allocated with the supplied allocator
|
||||
@(require_results)
|
||||
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
|
||||
value, _ = lookup_env(key, allocator)
|
||||
return
|
||||
}
|
||||
|
||||
// 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)
|
||||
v := win32.utf8_to_wstring(value)
|
||||
|
||||
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 :: proc(key: string) -> Errno {
|
||||
unset_env :: proc(key: string) -> Error {
|
||||
k := win32.utf8_to_wstring(key)
|
||||
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"
|
||||
// NOTE: the slice of strings and the strings with be allocated using the supplied allocator
|
||||
@(require_results)
|
||||
environ :: proc(allocator := context.allocator) -> []string {
|
||||
envs := cast([^]win32.WCHAR)(win32.GetEnvironmentStringsW())
|
||||
envs := ([^]win32.WCHAR)(win32.GetEnvironmentStringsW())
|
||||
if envs == nil {
|
||||
return nil
|
||||
}
|
||||
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 {
|
||||
if c := envs[i]; c == 0 {
|
||||
if i <= from {
|
||||
|
||||
322
core/os/errors.odin
Normal file
322
core/os/errors.odin
Normal 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
|
||||
}
|
||||
@@ -5,13 +5,15 @@ import "base:intrinsics"
|
||||
import "base:runtime"
|
||||
import "core:unicode/utf16"
|
||||
|
||||
@(require_results)
|
||||
is_path_separator :: proc(c: byte) -> bool {
|
||||
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 {
|
||||
return INVALID_HANDLE, ERROR_FILE_NOT_FOUND
|
||||
return INVALID_HANDLE, General_Error.Not_Exist
|
||||
}
|
||||
|
||||
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)
|
||||
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 {
|
||||
return handle, ERROR_NONE
|
||||
return handle, nil
|
||||
}
|
||||
|
||||
err := Errno(win32.GetLastError())
|
||||
return INVALID_HANDLE, err
|
||||
return INVALID_HANDLE, get_last_error()
|
||||
}
|
||||
|
||||
close :: proc(fd: Handle) -> Errno {
|
||||
close :: proc(fd: Handle) -> Error {
|
||||
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)) {
|
||||
err = Errno(win32.GetLastError())
|
||||
err = get_last_error()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
write :: proc(fd: Handle, data: []byte) -> (int, Error) {
|
||||
if len(data) == 0 {
|
||||
return 0, ERROR_NONE
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
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)
|
||||
if single_write_length <= 0 || !e {
|
||||
err := Errno(win32.GetLastError())
|
||||
return int(total_write), err
|
||||
return int(total_write), get_last_error()
|
||||
}
|
||||
total_write += i64(single_write_length)
|
||||
}
|
||||
return int(total_write), ERROR_NONE
|
||||
return int(total_write), nil
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) {
|
||||
@(private="file", require_results)
|
||||
read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Error) {
|
||||
if len(b) == 0 {
|
||||
return 0, 0
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
BUF_SIZE :: 386
|
||||
buf16: [BUF_SIZE]u16
|
||||
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)
|
||||
max_read := u32(min(BUF_SIZE, min_read))
|
||||
if max_read == 0 {
|
||||
@@ -118,7 +118,7 @@ read_console :: proc(handle: win32.HANDLE, b: []byte) -> (n: int, err: Errno) {
|
||||
single_read_length: u32
|
||||
ok := win32.ReadConsoleW(handle, &buf16[0], max_read, &single_read_length, nil)
|
||||
if !ok {
|
||||
err = Errno(win32.GetLastError())
|
||||
err = get_last_error()
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
return 0, ERROR_NONE
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
handle := win32.HANDLE(fd)
|
||||
@@ -165,7 +165,7 @@ read :: proc(fd: Handle, data: []byte) -> (total_read: int, err: Errno) {
|
||||
|
||||
if is_console {
|
||||
total_read, err = read_console(handle, data[total_read:][:to_read])
|
||||
if err != 0 {
|
||||
if err != nil {
|
||||
return total_read, err
|
||||
}
|
||||
} 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:
|
||||
// https://learn.microsoft.com/en-us/windows/win32/fileio/testing-for-the-end-of-a-file
|
||||
if bytes_read == 0 {
|
||||
return 0, ERROR_HANDLE_EOF
|
||||
return 0, .EOF
|
||||
} else {
|
||||
return int(bytes_read), ERROR_NONE
|
||||
return int(bytes_read), nil
|
||||
}
|
||||
} 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
|
||||
switch whence {
|
||||
case 0: w = win32.FILE_BEGIN
|
||||
@@ -197,22 +197,23 @@ seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
|
||||
lo := i32(offset)
|
||||
ft := win32.GetFileType(win32.HANDLE(fd))
|
||||
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)
|
||||
if dw_ptr == win32.INVALID_SET_FILE_POINTER {
|
||||
err := Errno(win32.GetLastError())
|
||||
err := get_last_error()
|
||||
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
|
||||
err: Errno
|
||||
err: Error
|
||||
if !win32.GetFileSizeEx(win32.HANDLE(fd), &length) {
|
||||
err = Errno(win32.GetLastError())
|
||||
err = get_last_error()
|
||||
}
|
||||
return i64(length), err
|
||||
}
|
||||
@@ -220,10 +221,9 @@ file_size :: proc(fd: Handle) -> (i64, Errno) {
|
||||
|
||||
@(private)
|
||||
MAX_RW :: 1<<30
|
||||
ERROR_EOF :: 38
|
||||
|
||||
@(private)
|
||||
pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
|
||||
pread :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Error) {
|
||||
buf := data
|
||||
if len(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)
|
||||
done: win32.DWORD
|
||||
e: Errno
|
||||
e: Error
|
||||
if !win32.ReadFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
|
||||
e = Errno(win32.GetLastError())
|
||||
e = get_last_error()
|
||||
done = 0
|
||||
}
|
||||
return int(done), e
|
||||
}
|
||||
@(private)
|
||||
pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Errno) {
|
||||
pwrite :: proc(fd: Handle, data: []byte, offset: i64) -> (int, Error) {
|
||||
buf := data
|
||||
if len(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)
|
||||
done: win32.DWORD
|
||||
e: Errno
|
||||
e: Error
|
||||
if !win32.WriteFile(h, raw_data(buf), u32(len(buf)), &done, &o) {
|
||||
e = Errno(win32.GetLastError())
|
||||
e = get_last_error()
|
||||
done = 0
|
||||
}
|
||||
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
|
||||
*/
|
||||
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 {
|
||||
return 0, ERROR_NEGATIVE_OFFSET
|
||||
return 0, .Invalid_Offset
|
||||
}
|
||||
|
||||
b, offset := data, offset
|
||||
for len(b) > 0 {
|
||||
m, e := pread(fd, b, offset)
|
||||
if e == ERROR_EOF {
|
||||
err = 0
|
||||
err = nil
|
||||
break
|
||||
}
|
||||
if e != 0 {
|
||||
if e != nil {
|
||||
err = e
|
||||
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
|
||||
*/
|
||||
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 {
|
||||
return 0, ERROR_NEGATIVE_OFFSET
|
||||
return 0, .Invalid_Offset
|
||||
}
|
||||
|
||||
b, offset := data, offset
|
||||
for len(b) > 0 {
|
||||
m, e := pwrite(fd, b, offset)
|
||||
if e != 0 {
|
||||
err = e
|
||||
break
|
||||
}
|
||||
m := pwrite(fd, b, offset) or_return
|
||||
n += m
|
||||
b = b[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))
|
||||
|
||||
|
||||
@(require_results)
|
||||
get_std_handle :: proc "contextless" (h: uint) -> Handle {
|
||||
fd := win32.GetStdHandle(win32.DWORD(h))
|
||||
return Handle(fd)
|
||||
@@ -352,6 +349,7 @@ exists :: proc(path: string) -> bool {
|
||||
return attribs != win32.INVALID_FILE_ATTRIBUTES
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
is_file :: proc(path: string) -> bool {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
@@ -363,6 +361,7 @@ is_file :: proc(path: string) -> bool {
|
||||
return false
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
is_dir :: proc(path: string) -> bool {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
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
|
||||
@private cwd_lock := win32.SRWLOCK{} // zero is initialized
|
||||
|
||||
@(require_results)
|
||||
get_current_directory :: proc(allocator := context.allocator) -> string {
|
||||
win32.AcquireSRWLockExclusive(&cwd_lock)
|
||||
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == allocator)
|
||||
|
||||
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))
|
||||
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 ""
|
||||
}
|
||||
|
||||
set_current_directory :: proc(path: string) -> (err: Errno) {
|
||||
set_current_directory :: proc(path: string) -> (err: Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
wstr := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
|
||||
win32.AcquireSRWLockExclusive(&cwd_lock)
|
||||
|
||||
if !win32.SetCurrentDirectoryW(wstr) {
|
||||
err = Errno(win32.GetLastError())
|
||||
err = get_last_error()
|
||||
}
|
||||
|
||||
win32.ReleaseSRWLockExclusive(&cwd_lock)
|
||||
@@ -409,31 +409,31 @@ set_current_directory :: proc(path: string) -> (err: Errno) {
|
||||
}
|
||||
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()
|
||||
// Mode is unused on Windows, but is needed on *nix
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
|
||||
if !win32.CreateDirectoryW(wpath, nil) {
|
||||
err = Errno(win32.GetLastError())
|
||||
err = get_last_error()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
remove_directory :: proc(path: string) -> (err: Errno) {
|
||||
remove_directory :: proc(path: string) -> (err: Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
|
||||
if !win32.RemoveDirectoryW(wpath) {
|
||||
err = Errno(win32.GetLastError())
|
||||
err = get_last_error()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
@(private)
|
||||
@(private, require_results)
|
||||
is_abs :: proc(path: string) -> bool {
|
||||
if len(path) > 0 && path[0] == '/' {
|
||||
return true
|
||||
@@ -449,7 +449,7 @@ is_abs :: proc(path: string) -> bool {
|
||||
return false
|
||||
}
|
||||
|
||||
@(private)
|
||||
@(private, require_results)
|
||||
fix_long_path :: proc(path: string) -> string {
|
||||
if len(path) < 248 {
|
||||
return path
|
||||
@@ -464,7 +464,7 @@ fix_long_path :: proc(path: string) -> string {
|
||||
|
||||
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)
|
||||
n := len(path)
|
||||
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()
|
||||
n := win32.utf8_to_wstring(fix_long_path(new_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()
|
||||
wpath := win32.utf8_to_wstring(path, context.temp_allocator)
|
||||
|
||||
if !win32.DeleteFileW(wpath) {
|
||||
err = Errno(win32.GetLastError())
|
||||
err = get_last_error()
|
||||
}
|
||||
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()
|
||||
from := win32.utf8_to_wstring(old_path, context.temp_allocator)
|
||||
to := win32.utf8_to_wstring(new_path, context.temp_allocator)
|
||||
|
||||
if !win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING) {
|
||||
err = Errno(win32.GetLastError())
|
||||
err = get_last_error()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
ftruncate :: proc(fd: Handle, length: i64) -> (err: Errno) {
|
||||
curr_off, e := seek(fd, 0, 1)
|
||||
if e != 0 {
|
||||
return e
|
||||
}
|
||||
ftruncate :: proc(fd: Handle, length: i64) -> (err: Error) {
|
||||
curr_off := seek(fd, 0, 1) or_return
|
||||
defer seek(fd, curr_off, 0)
|
||||
_, e = seek(fd, length, 0)
|
||||
if e != 0 {
|
||||
return e
|
||||
}
|
||||
_= seek(fd, length, 0) or_return
|
||||
ok := win32.SetEndOfFile(win32.HANDLE(fd))
|
||||
if !ok {
|
||||
return Errno(win32.GetLastError())
|
||||
return get_last_error()
|
||||
}
|
||||
return ERROR_NONE
|
||||
return nil
|
||||
}
|
||||
|
||||
truncate :: proc(path: string, length: i64) -> (err: Errno) {
|
||||
fd: Handle
|
||||
fd, err = open(path, O_WRONLY|O_CREATE, 0o666)
|
||||
if err != 0 {
|
||||
return
|
||||
}
|
||||
truncate :: proc(path: string, length: i64) -> (err: Error) {
|
||||
fd := open(path, O_WRONLY|O_CREATE, 0o666) or_return
|
||||
defer close(fd)
|
||||
err = ftruncate(fd, length)
|
||||
return
|
||||
return ftruncate(fd, length)
|
||||
}
|
||||
|
||||
|
||||
remove :: proc(name: string) -> Errno {
|
||||
remove :: proc(name: string) -> Error {
|
||||
p := win32.utf8_to_wstring(fix_long_path(name))
|
||||
err, err1: win32.DWORD
|
||||
if !win32.DeleteFileW(p) {
|
||||
err = win32.GetLastError()
|
||||
}
|
||||
if err == 0 {
|
||||
return 0
|
||||
return nil
|
||||
}
|
||||
if !win32.RemoveDirectoryW(p) {
|
||||
err1 = win32.GetLastError()
|
||||
}
|
||||
if err1 == 0 {
|
||||
return 0
|
||||
return nil
|
||||
}
|
||||
|
||||
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.nLength = size_of(win32.SECURITY_ATTRIBUTES)
|
||||
sa.bInheritHandle = true
|
||||
if !win32.CreatePipe((^win32.HANDLE)(&r), (^win32.HANDLE)(&w), &sa, 0) {
|
||||
err = Errno(win32.GetLastError())
|
||||
err = get_last_error()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
164
core/os/os.odin
164
core/os/os.odin
@@ -1,6 +1,8 @@
|
||||
package os
|
||||
|
||||
import "base:intrinsics"
|
||||
import "base:runtime"
|
||||
import "core:io"
|
||||
import "core:strconv"
|
||||
import "core:unicode/utf8"
|
||||
|
||||
@@ -13,15 +15,15 @@ SEEK_SET :: 0
|
||||
SEEK_CUR :: 1
|
||||
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)
|
||||
}
|
||||
|
||||
write_byte :: proc(fd: Handle, b: byte) -> (int, Errno) {
|
||||
write_byte :: proc(fd: Handle, b: byte) -> (int, Error) {
|
||||
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 {
|
||||
return write_byte(fd, byte(r))
|
||||
}
|
||||
@@ -30,105 +32,94 @@ write_rune :: proc(fd: Handle, r: rune) -> (int, Errno) {
|
||||
return write(fd, b[:n])
|
||||
}
|
||||
|
||||
write_encoded_rune :: proc(fd: Handle, r: rune) {
|
||||
write_byte(fd, '\'')
|
||||
write_encoded_rune :: proc(f: Handle, r: rune) -> (n: int, err: Error) {
|
||||
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 {
|
||||
case '\a': write_string(fd, "\\a")
|
||||
case '\b': write_string(fd, "\\b")
|
||||
case '\e': write_string(fd, "\\e")
|
||||
case '\f': write_string(fd, "\\f")
|
||||
case '\n': write_string(fd, "\\n")
|
||||
case '\r': write_string(fd, "\\r")
|
||||
case '\t': write_string(fd, "\\t")
|
||||
case '\v': write_string(fd, "\\v")
|
||||
case '\a': if wrap(write_string(f, "\\a"), &n, &err) { return }
|
||||
case '\b': if wrap(write_string(f, "\\b"), &n, &err) { return }
|
||||
case '\e': if wrap(write_string(f, "\\e"), &n, &err) { return }
|
||||
case '\f': if wrap(write_string(f, "\\f"), &n, &err) { return }
|
||||
case '\n': if wrap(write_string(f, "\\n"), &n, &err) { return }
|
||||
case '\r': if wrap(write_string(f, "\\r"), &n, &err) { return }
|
||||
case '\t': if wrap(write_string(f, "\\t"), &n, &err) { return }
|
||||
case '\v': if wrap(write_string(f, "\\v"), &n, &err) { return }
|
||||
case:
|
||||
if r < 32 {
|
||||
write_string(fd, "\\x")
|
||||
if wrap(write_string(f, "\\x"), &n, &err) { return }
|
||||
b: [2]byte
|
||||
s := strconv.append_bits(b[:], u64(r), 16, true, 64, strconv.digits, nil)
|
||||
switch len(s) {
|
||||
case 0: write_string(fd, "00")
|
||||
case 1: write_rune(fd, '0')
|
||||
case 2: write_string(fd, s)
|
||||
case 0: if wrap(write_string(f, "00"), &n, &err) { return }
|
||||
case 1: if wrap(write_rune(f, '0'), &n, &err) { return }
|
||||
case 2: if wrap(write_string(f, s), &n, &err) { return }
|
||||
}
|
||||
} else {
|
||||
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 {
|
||||
return 0, -1
|
||||
return 0, io.Error.Short_Buffer
|
||||
}
|
||||
nn := max(int)
|
||||
for nn > 0 && n < min && err == 0 {
|
||||
for nn > 0 && n < min && err == nil {
|
||||
nn, err = read(fd, buf[n:])
|
||||
n += nn
|
||||
}
|
||||
if n >= min {
|
||||
err = 0
|
||||
err = nil
|
||||
}
|
||||
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))
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
file_size_from_path :: proc(path: string) -> i64 {
|
||||
fd, err := open(path, O_RDONLY, 0)
|
||||
if err != 0 {
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
defer close(fd)
|
||||
|
||||
length: i64
|
||||
if length, err = file_size(fd); err != 0 {
|
||||
if length, err = file_size(fd); err != nil {
|
||||
return -1
|
||||
}
|
||||
return length
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
read_entire_file_from_filename :: proc(name: string, allocator := context.allocator, loc := #caller_location) -> (data: []byte, success: bool) {
|
||||
context.allocator = allocator
|
||||
|
||||
fd, err := open(name, O_RDONLY, 0)
|
||||
if err != 0 {
|
||||
return nil, false
|
||||
}
|
||||
defer close(fd)
|
||||
|
||||
return read_entire_file_from_handle(fd, allocator, loc)
|
||||
err: Error
|
||||
data, err = read_entire_file_from_filename_or_err(name, allocator, loc)
|
||||
success = err == nil
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
read_entire_file_from_handle :: proc(fd: Handle, allocator := context.allocator, loc := #caller_location) -> (data: []byte, success: bool) {
|
||||
context.allocator = allocator
|
||||
|
||||
length: i64
|
||||
err: Errno
|
||||
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
|
||||
err: Error
|
||||
data, err = read_entire_file_from_handle_or_err(fd, allocator, loc)
|
||||
success = err == nil
|
||||
return
|
||||
}
|
||||
|
||||
read_entire_file :: proc {
|
||||
@@ -136,7 +127,50 @@ read_entire_file :: proc {
|
||||
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) {
|
||||
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
|
||||
if truncate {
|
||||
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
|
||||
}
|
||||
|
||||
fd, err := open(name, flags, mode)
|
||||
if err != 0 {
|
||||
return false
|
||||
}
|
||||
fd := open(name, flags, mode) or_return
|
||||
defer close(fd)
|
||||
|
||||
_, write_err := write(fd, data)
|
||||
return write_err == 0
|
||||
_ = write(fd, data) or_return
|
||||
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])
|
||||
}
|
||||
|
||||
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])
|
||||
}
|
||||
|
||||
@@ -173,6 +204,7 @@ heap_alloc :: runtime.heap_alloc
|
||||
heap_resize :: runtime.heap_resize
|
||||
heap_free :: runtime.heap_free
|
||||
|
||||
@(require_results)
|
||||
processor_core_count :: proc() -> int {
|
||||
return _processor_core_count()
|
||||
}
|
||||
|
||||
@@ -42,13 +42,14 @@ Error :: union #shared_nil {
|
||||
ERROR_NONE :: Error{}
|
||||
|
||||
|
||||
|
||||
@(require_results)
|
||||
is_platform_error :: proc(ferr: Error) -> (err: i32, ok: bool) {
|
||||
v := ferr.(Platform_Error) or_else {}
|
||||
return i32(v), i32(v) != 0
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
error_string :: proc(ferr: Error) -> string {
|
||||
if ferr == nil {
|
||||
return ""
|
||||
|
||||
@@ -4,8 +4,8 @@ package os2
|
||||
import "core:sys/linux"
|
||||
|
||||
@(rodata)
|
||||
_errno_strings : [linux.Errno]string = {
|
||||
.NONE = "Success",
|
||||
_errno_strings := [linux.Error]string{
|
||||
.NONE = "",
|
||||
.EPERM = "Operation not permitted",
|
||||
.ENOENT = "No such file or directory",
|
||||
.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 {
|
||||
case .NONE:
|
||||
return nil
|
||||
@@ -158,8 +158,8 @@ _get_platform_error :: proc(errno: linux.Errno) -> Error {
|
||||
}
|
||||
|
||||
_error_string :: proc(errno: i32) -> string {
|
||||
if errno >= 0 && errno <= i32(max(linux.Errno)) {
|
||||
return _errno_strings[linux.Errno(errno)]
|
||||
if errno >= 0 && errno <= i32(max(linux.Error)) {
|
||||
return _errno_strings[linux.Error(errno)]
|
||||
}
|
||||
return "Unknown Error"
|
||||
}
|
||||
|
||||
@@ -73,6 +73,24 @@ write_encoded_rune :: proc(f: ^File, r: rune) -> (n: int, err: Error) {
|
||||
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) {
|
||||
return write(f, ([^]byte)(data)[:len])
|
||||
@@ -155,11 +173,8 @@ write_entire_file :: proc(name: string, data: []byte, perm: int, truncate := tru
|
||||
if truncate {
|
||||
flags |= O_TRUNC
|
||||
}
|
||||
f, err := open(name, flags, perm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = write(f, data)
|
||||
f := open(name, flags, perm) or_return
|
||||
_, err := write(f, data)
|
||||
if cerr := close(f); cerr != nil && err == nil {
|
||||
err = cerr
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ _mkdir_all :: proc(path: string, perm: int) -> Error {
|
||||
path_bytes[len(path)] = 0
|
||||
|
||||
dfd: linux.Fd
|
||||
errno: linux.Errno
|
||||
errno: linux.Error
|
||||
if path_bytes[0] == '/' {
|
||||
dfd, errno = linux.open("/", _OPENDIR_FLAGS)
|
||||
path_bytes = path_bytes[1:]
|
||||
|
||||
@@ -6,25 +6,22 @@ import "core:time"
|
||||
import "core:strings"
|
||||
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 {
|
||||
return {}, nil
|
||||
return
|
||||
}
|
||||
|
||||
path, err := _cleanpath_from_handle(f, allocator)
|
||||
if err != nil {
|
||||
return {}, err
|
||||
}
|
||||
path := _cleanpath_from_handle(f, allocator) or_return
|
||||
|
||||
h := _handle(f)
|
||||
switch win32.GetFileType(h) {
|
||||
case win32.FILE_TYPE_PIPE, win32.FILE_TYPE_CHAR:
|
||||
fi := File_Info {
|
||||
fi = File_Info {
|
||||
fullpath = path,
|
||||
name = basename(path),
|
||||
type = file_type(h),
|
||||
}
|
||||
return fi, nil
|
||||
return
|
||||
}
|
||||
|
||||
return _file_info_from_get_file_information_by_handle(path, h, allocator)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,54 +2,59 @@ package os
|
||||
|
||||
import "core:sys/es"
|
||||
|
||||
Handle :: distinct int;
|
||||
Errno :: distinct int;
|
||||
Handle :: distinct int
|
||||
_Platform_Error :: enum i32 {NONE}
|
||||
|
||||
ERROR_NONE :: (Errno) (es.SUCCESS);
|
||||
// ERROR_NONE :: Error(es.SUCCESS)
|
||||
|
||||
O_RDONLY :: 0x1;
|
||||
O_WRONLY :: 0x2;
|
||||
O_CREATE :: 0x4;
|
||||
O_TRUNC :: 0x8;
|
||||
O_RDONLY :: 0x1
|
||||
O_WRONLY :: 0x2
|
||||
O_CREATE :: 0x4
|
||||
O_TRUNC :: 0x8
|
||||
|
||||
stderr : Handle = 0;
|
||||
stderr : Handle = 0
|
||||
|
||||
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 {
|
||||
return es.HeapAllocate(size, zero_memory);
|
||||
return es.HeapAllocate(size, zero_memory)
|
||||
}
|
||||
|
||||
heap_free :: proc(ptr: rawptr) {
|
||||
es.HeapFree(ptr);
|
||||
es.HeapFree(ptr)
|
||||
}
|
||||
|
||||
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) {
|
||||
return (Handle) (0), (Errno) (1);
|
||||
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Error) {
|
||||
return (Handle) (0), (Error) (1)
|
||||
}
|
||||
|
||||
close :: proc(fd: Handle) -> Errno {
|
||||
return (Errno) (1);
|
||||
close :: proc(fd: Handle) -> Error {
|
||||
return (Error) (1)
|
||||
}
|
||||
|
||||
file_size :: proc(fd: Handle) -> (i64, Errno) {
|
||||
return (i64) (0), (Errno) (1);
|
||||
file_size :: proc(fd: Handle) -> (i64, Error) {
|
||||
return (i64) (0), (Error) (1)
|
||||
}
|
||||
|
||||
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
return (int) (0), (Errno) (1);
|
||||
read :: proc(fd: Handle, data: []byte) -> (int, Error) {
|
||||
return (int) (0), (Error) (1)
|
||||
}
|
||||
|
||||
write :: proc(fd: Handle, data: []u8) -> (int, Errno) {
|
||||
return (int) (0), (Errno) (1);
|
||||
write :: proc(fd: Handle, data: []u8) -> (int, Error) {
|
||||
return (int) (0), (Error) (1)
|
||||
}
|
||||
|
||||
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
|
||||
return (i64) (0), (Errno) (1);
|
||||
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Error) {
|
||||
return (i64) (0), (Error) (1)
|
||||
}
|
||||
|
||||
flush :: proc(fd: Handle) -> Error {
|
||||
// do nothing
|
||||
return nil
|
||||
}
|
||||
@@ -9,105 +9,200 @@ import "core:c"
|
||||
|
||||
Handle :: distinct i32
|
||||
File_Time :: distinct u64
|
||||
Errno :: distinct i32
|
||||
|
||||
INVALID_HANDLE :: ~Handle(0)
|
||||
|
||||
ERROR_NONE: Errno : 0
|
||||
EPERM: Errno : 1
|
||||
ENOENT: Errno : 2
|
||||
ESRCH: Errno : 3
|
||||
EINTR: Errno : 4
|
||||
EIO: Errno : 5
|
||||
ENXIO: Errno : 6
|
||||
E2BIG: Errno : 7
|
||||
ENOEXEC: Errno : 8
|
||||
EBADF: Errno : 9
|
||||
ECHILD: Errno : 10
|
||||
EBEADLK: Errno : 11
|
||||
ENOMEM: Errno : 12
|
||||
EACCESS: Errno : 13
|
||||
EFAULT: Errno : 14
|
||||
ENOTBLK: Errno : 15
|
||||
EBUSY: Errno : 16
|
||||
EEXIST: Errno : 17
|
||||
EXDEV: Errno : 18
|
||||
ENODEV: Errno : 19
|
||||
ENOTDIR: Errno : 20
|
||||
EISDIR: Errno : 21
|
||||
EINVAL: Errno : 22
|
||||
ENFILE: Errno : 23
|
||||
EMFILE: Errno : 24
|
||||
ENOTTY: Errno : 25
|
||||
ETXTBSY: Errno : 26
|
||||
EFBIG: Errno : 27
|
||||
ENOSPC: Errno : 28
|
||||
ESPIPE: Errno : 29
|
||||
EROFS: Errno : 30
|
||||
EMLINK: Errno : 31
|
||||
EPIPE: Errno : 32
|
||||
EDOM: Errno : 33
|
||||
ERANGE: Errno : 34 /* Result too large */
|
||||
EAGAIN: Errno : 35
|
||||
EINPROGRESS: Errno : 36
|
||||
EALREADY: Errno : 37
|
||||
ENOTSOCK: Errno : 38
|
||||
EDESTADDRREQ: Errno : 39
|
||||
EMSGSIZE: Errno : 40
|
||||
EPROTOTYPE: Errno : 41
|
||||
ENOPROTOOPT: Errno : 42
|
||||
EPROTONOSUPPORT: Errno : 43
|
||||
ESOCKTNOSUPPORT: Errno : 44
|
||||
EOPNOTSUPP: Errno : 45
|
||||
EPFNOSUPPORT: Errno : 46
|
||||
EAFNOSUPPORT: Errno : 47
|
||||
EADDRINUSE: Errno : 48
|
||||
EADDRNOTAVAIL: Errno : 49
|
||||
ENETDOWN: Errno : 50
|
||||
ENETUNREACH: Errno : 51
|
||||
ENETRESET: Errno : 52
|
||||
ECONNABORTED: Errno : 53
|
||||
ECONNRESET: Errno : 54
|
||||
ENOBUFS: Errno : 55
|
||||
EISCONN: Errno : 56
|
||||
ENOTCONN: Errno : 57
|
||||
ESHUTDOWN: Errno : 58
|
||||
ETIMEDOUT: Errno : 60
|
||||
ECONNREFUSED: Errno : 61
|
||||
ELOOP: Errno : 62
|
||||
ENAMETOOLING: Errno : 63
|
||||
EHOSTDOWN: Errno : 64
|
||||
EHOSTUNREACH: Errno : 65
|
||||
ENOTEMPTY: Errno : 66
|
||||
EPROCLIM: Errno : 67
|
||||
EUSERS: Errno : 68
|
||||
EDQUOT: Errno : 69
|
||||
ESTALE: Errno : 70
|
||||
EBADRPC: Errno : 72
|
||||
ERPCMISMATCH: Errno : 73
|
||||
EPROGUNAVAIL: Errno : 74
|
||||
EPROGMISMATCH: Errno : 75
|
||||
EPROCUNAVAIL: Errno : 76
|
||||
ENOLCK: Errno : 77
|
||||
ENOSYS: Errno : 78
|
||||
EFTYPE: Errno : 79
|
||||
EAUTH: Errno : 80
|
||||
ENEEDAUTH: Errno : 81
|
||||
EIDRM: Errno : 82
|
||||
ENOMSG: Errno : 83
|
||||
EOVERFLOW: Errno : 84
|
||||
ECANCELED: Errno : 85
|
||||
EILSEQ: Errno : 86
|
||||
ENOATTR: Errno : 87
|
||||
EDOOFUS: Errno : 88
|
||||
EBADMSG: Errno : 89
|
||||
EMULTIHOP: Errno : 90
|
||||
ENOLINK: Errno : 91
|
||||
EPROTO: Errno : 92
|
||||
ENOTCAPABLE: Errno : 93
|
||||
ECAPMODE: Errno : 94
|
||||
ENOTRECOVERABLE: Errno : 95
|
||||
EOWNERDEAD: Errno : 96
|
||||
_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,
|
||||
EBEADLK = 11,
|
||||
ENOMEM = 12,
|
||||
EACCESS = 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, /* Result too large */
|
||||
EAGAIN = 35,
|
||||
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,
|
||||
ETIMEDOUT = 60,
|
||||
ECONNREFUSED = 61,
|
||||
ELOOP = 62,
|
||||
ENAMETOOLING = 63,
|
||||
EHOSTDOWN = 64,
|
||||
EHOSTUNREACH = 65,
|
||||
ENOTEMPTY = 66,
|
||||
EPROCLIM = 67,
|
||||
EUSERS = 68,
|
||||
EDQUOT = 69,
|
||||
ESTALE = 70,
|
||||
EBADRPC = 72,
|
||||
ERPCMISMATCH = 73,
|
||||
EPROGUNAVAIL = 74,
|
||||
EPROGMISMATCH = 75,
|
||||
EPROCUNAVAIL = 76,
|
||||
ENOLCK = 77,
|
||||
ENOSYS = 78,
|
||||
EFTYPE = 79,
|
||||
EAUTH = 80,
|
||||
ENEEDAUTH = 81,
|
||||
EIDRM = 82,
|
||||
ENOMSG = 83,
|
||||
EOVERFLOW = 84,
|
||||
ECANCELED = 85,
|
||||
EILSEQ = 86,
|
||||
ENOATTR = 87,
|
||||
EDOOFUS = 88,
|
||||
EBADMSG = 89,
|
||||
EMULTIHOP = 90,
|
||||
ENOLINK = 91,
|
||||
EPROTO = 92,
|
||||
ENOTCAPABLE = 93,
|
||||
ECAPMODE = 94,
|
||||
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_WRONLY :: 0x00001
|
||||
@@ -258,13 +353,13 @@ S_ISGID :: 0o2000 // Set group id on execution
|
||||
S_ISVTX :: 0o1000 // Directory restrcted delete
|
||||
|
||||
|
||||
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 }
|
||||
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 }
|
||||
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 }
|
||||
S_ISSOCK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFSOCK }
|
||||
@(require_results) S_ISLNK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFLNK }
|
||||
@(require_results) S_ISREG :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFREG }
|
||||
@(require_results) S_ISDIR :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFDIR }
|
||||
@(require_results) S_ISCHR :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFCHR }
|
||||
@(require_results) S_ISBLK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFBLK }
|
||||
@(require_results) S_ISFIFO :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFIFO }
|
||||
@(require_results) S_ISSOCK :: #force_inline proc(m: mode_t) -> bool { return (m & S_IFMT) == S_IFSOCK }
|
||||
|
||||
F_OK :: 0 // Test for file existance
|
||||
X_OK :: 1 // Test for execute permission
|
||||
@@ -274,7 +369,7 @@ R_OK :: 4 // Test for read permission
|
||||
F_KINFO :: 22
|
||||
|
||||
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="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 ---
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
is_path_separator :: proc(r: rune) -> bool {
|
||||
return r == '/'
|
||||
}
|
||||
|
||||
get_last_error :: proc "contextless" () -> int {
|
||||
return int(__errno_location()^)
|
||||
@(require_results, no_instrumentation)
|
||||
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()
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
handle := _unix_open(cstr, c.int(flags), c.int(mode))
|
||||
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)
|
||||
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`.
|
||||
@@ -354,124 +457,148 @@ close :: proc(fd: Handle) -> Errno {
|
||||
@(private)
|
||||
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)
|
||||
bytes_read := _unix_read(fd, &data[0], to_read)
|
||||
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 {
|
||||
return 0, ERROR_NONE
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
to_write := min(c.size_t(len(data)), MAX_RW)
|
||||
bytes_written := _unix_write(fd, &data[0], to_write)
|
||||
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))
|
||||
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) {
|
||||
s, err := _fstat(fd)
|
||||
if err != ERROR_NONE {
|
||||
return -1, err
|
||||
}
|
||||
return s.size, ERROR_NONE
|
||||
@(require_results)
|
||||
file_size :: proc(fd: Handle) -> (size: i64, err: Error) {
|
||||
size = -1
|
||||
s := _fstat(fd) or_return
|
||||
size = s.size
|
||||
return
|
||||
}
|
||||
|
||||
rename :: proc(old_path, new_path: string) -> Errno {
|
||||
rename :: proc(old_path, new_path: string) -> Error {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
old_path_cstr := strings.clone_to_cstring(old_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)
|
||||
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()
|
||||
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
res := _unix_unlink(path_cstr)
|
||||
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()
|
||||
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
res := _unix_mkdir(path_cstr, mode)
|
||||
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()
|
||||
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
res := _unix_rmdir(path_cstr)
|
||||
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 {
|
||||
s, err := _fstat(fd)
|
||||
if err != ERROR_NONE {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return S_ISREG(s.mode)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
is_file_path :: proc(path: string, follow_links: bool = true) -> bool {
|
||||
s: OS_Stat
|
||||
err: Errno
|
||||
err: Error
|
||||
if follow_links {
|
||||
s, err = _stat(path)
|
||||
} else {
|
||||
s, err = _lstat(path)
|
||||
}
|
||||
if err != ERROR_NONE {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return S_ISREG(s.mode)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
is_dir_handle :: proc(fd: Handle) -> bool {
|
||||
s, err := _fstat(fd)
|
||||
if err != ERROR_NONE {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return S_ISDIR(s.mode)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
|
||||
s: OS_Stat
|
||||
err: Errno
|
||||
err: Error
|
||||
if follow_links {
|
||||
s, err = _stat(path)
|
||||
} else {
|
||||
s, err = _lstat(path)
|
||||
}
|
||||
if err != ERROR_NONE {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return S_ISDIR(s.mode)
|
||||
@@ -490,38 +617,40 @@ stderr: Handle = 2
|
||||
last_write_time :: proc(fd: Handle) -> 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)
|
||||
if err != ERROR_NONE {
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
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)
|
||||
if err != ERROR_NONE {
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
|
||||
return File_Time(modified), ERROR_NONE
|
||||
return File_Time(modified), nil
|
||||
}
|
||||
|
||||
@private
|
||||
_stat :: proc(path: string) -> (OS_Stat, Errno) {
|
||||
@(private, require_results)
|
||||
_stat :: proc(path: string) -> (OS_Stat, Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
s: OS_Stat = ---
|
||||
result := _unix_lstat(cstr, &s)
|
||||
if result == -1 {
|
||||
return s, Errno(get_last_error())
|
||||
return s, get_last_error()
|
||||
}
|
||||
return s, ERROR_NONE
|
||||
return s, nil
|
||||
}
|
||||
|
||||
@private
|
||||
_lstat :: proc(path: string) -> (OS_Stat, Errno) {
|
||||
@(private, require_results)
|
||||
_lstat :: proc(path: string) -> (OS_Stat, Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
|
||||
@@ -529,54 +658,53 @@ _lstat :: proc(path: string) -> (OS_Stat, Errno) {
|
||||
s: OS_Stat = ---
|
||||
res := _unix_lstat(cstr, &s)
|
||||
if res == -1 {
|
||||
return s, Errno(get_last_error())
|
||||
return s, get_last_error()
|
||||
}
|
||||
return s, ERROR_NONE
|
||||
return s, nil
|
||||
}
|
||||
|
||||
@private
|
||||
_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) {
|
||||
@(private, require_results)
|
||||
_fstat :: proc(fd: Handle) -> (OS_Stat, Error) {
|
||||
s: OS_Stat = ---
|
||||
result := _unix_fstat(fd, &s)
|
||||
if result == -1 {
|
||||
return s, Errno(get_last_error())
|
||||
return s, get_last_error()
|
||||
}
|
||||
return s, ERROR_NONE
|
||||
return s, nil
|
||||
}
|
||||
|
||||
@private
|
||||
_fdopendir :: proc(fd: Handle) -> (Dir, Errno) {
|
||||
@(private, require_results)
|
||||
_fdopendir :: proc(fd: Handle) -> (Dir, Error) {
|
||||
dirp := _unix_fdopendir(fd)
|
||||
if dirp == cast(Dir)nil {
|
||||
return nil, Errno(get_last_error())
|
||||
return nil, get_last_error()
|
||||
}
|
||||
return dirp, ERROR_NONE
|
||||
return dirp, nil
|
||||
}
|
||||
|
||||
@private
|
||||
_closedir :: proc(dirp: Dir) -> Errno {
|
||||
@(private)
|
||||
_closedir :: proc(dirp: Dir) -> Error {
|
||||
rc := _unix_closedir(dirp)
|
||||
if rc != 0 {
|
||||
return Errno(get_last_error())
|
||||
return get_last_error()
|
||||
}
|
||||
return ERROR_NONE
|
||||
return nil
|
||||
}
|
||||
|
||||
@private
|
||||
@(private)
|
||||
_rewinddir :: proc(dirp: Dir) {
|
||||
_unix_rewinddir(dirp)
|
||||
}
|
||||
|
||||
@private
|
||||
_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) {
|
||||
@(private, require_results)
|
||||
_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Error, end_of_stream: bool) {
|
||||
result: ^Dirent
|
||||
rc := _unix_readdir_r(dirp, &entry, &result)
|
||||
|
||||
if rc != 0 {
|
||||
err = Errno(get_last_error())
|
||||
err = get_last_error()
|
||||
return
|
||||
}
|
||||
err = ERROR_NONE
|
||||
|
||||
if result == nil {
|
||||
end_of_stream = true
|
||||
@@ -586,8 +714,8 @@ _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool)
|
||||
return
|
||||
}
|
||||
|
||||
@private
|
||||
_readlink :: proc(path: string) -> (string, Errno) {
|
||||
@(private, require_results)
|
||||
_readlink :: proc(path: string) -> (string, Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.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)
|
||||
if rc == -1 {
|
||||
delete(buf)
|
||||
return "", Errno(get_last_error())
|
||||
return "", get_last_error()
|
||||
} else if rc == int(bufsz) {
|
||||
bufsz += MAX_PATH
|
||||
delete(buf)
|
||||
buf = make([]byte, bufsz)
|
||||
} 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
|
||||
// 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
|
||||
@@ -626,14 +755,15 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) {
|
||||
|
||||
res := _unix_fcntl(fd, F_KINFO, cast(uintptr)&kinfo)
|
||||
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))
|
||||
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
|
||||
if rel == "" {
|
||||
rel = "."
|
||||
@@ -644,27 +774,28 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
|
||||
|
||||
path_ptr := _unix_realpath(rel_cstr, nil)
|
||||
if path_ptr == nil {
|
||||
return "", Errno(get_last_error())
|
||||
return "", get_last_error()
|
||||
}
|
||||
defer _unix_free(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()
|
||||
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
result := _unix_access(cstr, c.int(mask))
|
||||
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) {
|
||||
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
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
|
||||
value, _ = lookup_env(key, allocator)
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
get_current_directory :: proc() -> string {
|
||||
// NOTE(tetra): I would use PATH_MAX here, but I was not able to find
|
||||
// an authoritative value for it across all systems.
|
||||
@@ -692,7 +825,7 @@ get_current_directory :: proc() -> string {
|
||||
if cwd != nil {
|
||||
return string(cwd)
|
||||
}
|
||||
if Errno(get_last_error()) != ERANGE {
|
||||
if get_last_error() != ERANGE {
|
||||
delete(buf)
|
||||
return ""
|
||||
}
|
||||
@@ -701,14 +834,14 @@ get_current_directory :: proc() -> string {
|
||||
unreachable()
|
||||
}
|
||||
|
||||
set_current_directory :: proc(path: string) -> (err: Errno) {
|
||||
set_current_directory :: proc(path: string) -> (err: Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
res := _unix_chdir(cstr)
|
||||
if res == -1 {
|
||||
return Errno(get_last_error())
|
||||
return get_last_error()
|
||||
}
|
||||
return ERROR_NONE
|
||||
return nil
|
||||
}
|
||||
|
||||
exit :: proc "contextless" (code: int) -> ! {
|
||||
@@ -716,16 +849,19 @@ exit :: proc "contextless" (code: int) -> ! {
|
||||
_unix_exit(c.int(code))
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
current_thread_id :: proc "contextless" () -> int {
|
||||
return cast(int) pthread_getthreadid_np()
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
dlopen :: proc(filename: string, flags: int) -> rawptr {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
cstr := strings.clone_to_cstring(filename, context.temp_allocator)
|
||||
handle := _unix_dlopen(cstr, c.int(flags))
|
||||
return handle
|
||||
}
|
||||
@(require_results)
|
||||
dlsym :: proc(handle: rawptr, symbol: string) -> rawptr {
|
||||
assert(handle != nil)
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
@@ -741,6 +877,7 @@ dlerror :: proc() -> string {
|
||||
return string(_unix_dlerror())
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
get_page_size :: proc() -> int {
|
||||
// NOTE(tetra): The page size never changes, so why do anything complicated
|
||||
// if we don't have to.
|
||||
@@ -753,7 +890,7 @@ get_page_size :: proc() -> int {
|
||||
return page_size
|
||||
}
|
||||
|
||||
@(private)
|
||||
@(private, require_results)
|
||||
_processor_core_count :: proc() -> int {
|
||||
count : int = 0
|
||||
count_size := size_of(count)
|
||||
@@ -767,6 +904,7 @@ _processor_core_count :: proc() -> int {
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
_alloc_command_line_arguments :: proc() -> []string {
|
||||
res := make([]string, len(runtime.args__))
|
||||
for arg, i in runtime.args__ {
|
||||
|
||||
@@ -10,16 +10,14 @@ import "core:sys/haiku"
|
||||
Handle :: i32
|
||||
Pid :: i32
|
||||
File_Time :: i64
|
||||
Errno :: i32
|
||||
_Platform_Error :: haiku.Errno
|
||||
|
||||
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)
|
||||
|
||||
ERROR_NONE: Errno: 0
|
||||
|
||||
stdin: Handle = 0
|
||||
stdout: Handle = 1
|
||||
stderr: Handle = 2
|
||||
@@ -121,7 +119,7 @@ S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK
|
||||
|
||||
|
||||
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="getthrid") _unix_getthrid :: proc() -> int ---
|
||||
@@ -179,38 +177,47 @@ Dirent :: struct {
|
||||
|
||||
Dir :: distinct rawptr // DIR*
|
||||
|
||||
@(require_results)
|
||||
is_path_separator :: proc(r: rune) -> bool {
|
||||
return r == '/'
|
||||
}
|
||||
|
||||
get_last_error :: proc "contextless" () -> int {
|
||||
return int(__error()^)
|
||||
@(require_results, no_instrumentation)
|
||||
get_last_error :: proc "contextless" () -> Error {
|
||||
return Platform_Error(__error()^)
|
||||
}
|
||||
|
||||
fork :: proc() -> (Pid, Errno) {
|
||||
@(require_results)
|
||||
fork :: proc() -> (Pid, Error) {
|
||||
pid := _unix_fork()
|
||||
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()
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
handle := _unix_open(cstr, c.int(flags), c.int(mode))
|
||||
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)
|
||||
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,
|
||||
@@ -220,47 +227,69 @@ close :: proc(fd: Handle) -> Errno {
|
||||
@(private)
|
||||
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)
|
||||
bytes_read := _unix_read(fd, &data[0], to_read)
|
||||
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 {
|
||||
return 0, ERROR_NONE
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
to_write := min(c.size_t(len(data)), MAX_RW)
|
||||
bytes_written := _unix_write(fd, &data[0], to_write)
|
||||
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))
|
||||
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)
|
||||
if err != ERROR_NONE {
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return s.size, ERROR_NONE
|
||||
return s.size, nil
|
||||
}
|
||||
|
||||
// "Argv" arguments converted to Odin strings
|
||||
args := _alloc_command_line_arguments()
|
||||
|
||||
@(require_results)
|
||||
_alloc_command_line_arguments :: proc() -> []string {
|
||||
res := make([]string, len(runtime.args__))
|
||||
for arg, i in runtime.args__ {
|
||||
@@ -269,8 +298,8 @@ _alloc_command_line_arguments :: proc() -> []string {
|
||||
return res
|
||||
}
|
||||
|
||||
@private
|
||||
_stat :: proc(path: string) -> (OS_Stat, Errno) {
|
||||
@(private, require_results)
|
||||
_stat :: proc(path: string) -> (OS_Stat, Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
|
||||
@@ -278,13 +307,13 @@ _stat :: proc(path: string) -> (OS_Stat, Errno) {
|
||||
s: OS_Stat = ---
|
||||
res := _unix_stat(cstr, &s)
|
||||
if res == -1 {
|
||||
return s, Errno(get_last_error())
|
||||
return s, get_last_error()
|
||||
}
|
||||
return s, ERROR_NONE
|
||||
return s, nil
|
||||
}
|
||||
|
||||
@private
|
||||
_lstat :: proc(path: string) -> (OS_Stat, Errno) {
|
||||
@(private, require_results)
|
||||
_lstat :: proc(path: string) -> (OS_Stat, Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
|
||||
@@ -292,55 +321,54 @@ _lstat :: proc(path: string) -> (OS_Stat, Errno) {
|
||||
s: OS_Stat = ---
|
||||
res := _unix_lstat(cstr, &s)
|
||||
if res == -1 {
|
||||
return s, Errno(get_last_error())
|
||||
return s, get_last_error()
|
||||
}
|
||||
return s, ERROR_NONE
|
||||
return s, nil
|
||||
}
|
||||
|
||||
@private
|
||||
_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) {
|
||||
@(private, require_results)
|
||||
_fstat :: proc(fd: Handle) -> (OS_Stat, Error) {
|
||||
// deliberately uninitialized
|
||||
s: OS_Stat = ---
|
||||
res := _unix_fstat(fd, &s)
|
||||
if res == -1 {
|
||||
return s, Errno(get_last_error())
|
||||
return s, get_last_error()
|
||||
}
|
||||
return s, ERROR_NONE
|
||||
return s, nil
|
||||
}
|
||||
|
||||
@private
|
||||
_fdopendir :: proc(fd: Handle) -> (Dir, Errno) {
|
||||
@(private)
|
||||
_fdopendir :: proc(fd: Handle) -> (Dir, Error) {
|
||||
dirp := _unix_fdopendir(fd)
|
||||
if dirp == cast(Dir)nil {
|
||||
return nil, Errno(get_last_error())
|
||||
return nil, get_last_error()
|
||||
}
|
||||
return dirp, ERROR_NONE
|
||||
return dirp, nil
|
||||
}
|
||||
|
||||
@private
|
||||
_closedir :: proc(dirp: Dir) -> Errno {
|
||||
@(private)
|
||||
_closedir :: proc(dirp: Dir) -> Error {
|
||||
rc := _unix_closedir(dirp)
|
||||
if rc != 0 {
|
||||
return Errno(get_last_error())
|
||||
return get_last_error()
|
||||
}
|
||||
return ERROR_NONE
|
||||
return nil
|
||||
}
|
||||
|
||||
@private
|
||||
@(private)
|
||||
_rewinddir :: proc(dirp: Dir) {
|
||||
_unix_rewinddir(dirp)
|
||||
}
|
||||
|
||||
@private
|
||||
_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) {
|
||||
@(private, require_results)
|
||||
_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Error, end_of_stream: bool) {
|
||||
result: ^Dirent
|
||||
rc := _unix_readdir_r(dirp, &entry, &result)
|
||||
|
||||
if rc != 0 {
|
||||
err = Errno(get_last_error())
|
||||
err = get_last_error()
|
||||
return
|
||||
}
|
||||
err = ERROR_NONE
|
||||
|
||||
if result == nil {
|
||||
end_of_stream = true
|
||||
@@ -350,8 +378,8 @@ _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool)
|
||||
return
|
||||
}
|
||||
|
||||
@private
|
||||
_readlink :: proc(path: string) -> (string, Errno) {
|
||||
@(private, require_results)
|
||||
_readlink :: proc(path: string) -> (string, Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.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)
|
||||
if rc == -1 {
|
||||
delete(buf)
|
||||
return "", Errno(get_last_error())
|
||||
return "", get_last_error()
|
||||
} else if rc == int(bufsz) {
|
||||
bufsz += MAX_PATH
|
||||
delete(buf)
|
||||
buf = make([]byte, bufsz)
|
||||
} 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) {
|
||||
return "", Errno(ENOSYS)
|
||||
@(require_results)
|
||||
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
|
||||
if rel == "" {
|
||||
rel = "."
|
||||
@@ -387,26 +417,27 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
|
||||
|
||||
path_ptr := _unix_realpath(rel_cstr, nil)
|
||||
if path_ptr == nil {
|
||||
return "", Errno(get_last_error())
|
||||
return "", get_last_error()
|
||||
}
|
||||
defer _unix_free(path_ptr)
|
||||
|
||||
path_cstr := transmute(cstring)path_ptr
|
||||
path = strings.clone( string(path_cstr) )
|
||||
path_cstr := cstring(path_ptr)
|
||||
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()
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
res := _unix_access(cstr, c.int(mask))
|
||||
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) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == 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
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
|
||||
value, _ = lookup_env(key, allocator)
|
||||
return
|
||||
}
|
||||
|
||||
@(private)
|
||||
@(private, require_results)
|
||||
_processor_core_count :: proc() -> int {
|
||||
info: haiku.system_info
|
||||
haiku.get_system_info(&info)
|
||||
|
||||
@@ -3,42 +3,45 @@ package os
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
@(require_results)
|
||||
is_path_separator :: proc(c: byte) -> bool {
|
||||
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")
|
||||
}
|
||||
|
||||
close :: proc(fd: Handle) -> Errno {
|
||||
close :: proc(fd: Handle) -> Error {
|
||||
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")
|
||||
}
|
||||
|
||||
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
@(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")
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
@@ -47,38 +50,42 @@ file_size :: proc(fd: Handle) -> (i64, Errno) {
|
||||
MAX_RW :: 1<<30
|
||||
|
||||
@(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")
|
||||
}
|
||||
@(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")
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
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")
|
||||
}
|
||||
|
||||
stdout: Handle = 1
|
||||
stderr: Handle = 2
|
||||
|
||||
@(require_results)
|
||||
get_std_handle :: proc "contextless" (h: uint) -> Handle {
|
||||
context = runtime.default_context()
|
||||
unimplemented("core:os procedure not supported on JS target")
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
exists :: proc(path: string) -> bool {
|
||||
unimplemented("core:os procedure not supported on JS target")
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
is_file :: proc(path: string) -> bool {
|
||||
unimplemented("core:os procedure not supported on JS target")
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
is_dir :: proc(path: string) -> bool {
|
||||
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
|
||||
//@private cwd_lock := win32.SRWLOCK{} // zero is initialized
|
||||
|
||||
@(require_results)
|
||||
get_current_directory :: proc(allocator := context.allocator) -> string {
|
||||
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")
|
||||
}
|
||||
|
||||
|
||||
|
||||
change_directory :: proc(path: string) -> (err: Errno) {
|
||||
change_directory :: proc(path: string) -> (err: Error) {
|
||||
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")
|
||||
}
|
||||
|
||||
|
||||
remove_directory :: proc(path: string) -> (err: Errno) {
|
||||
remove_directory :: proc(path: string) -> (err: Error) {
|
||||
unimplemented("core:os procedure not supported on JS target")
|
||||
}
|
||||
|
||||
|
||||
|
||||
@(private)
|
||||
@(private, require_results)
|
||||
is_abs :: proc(path: string) -> bool {
|
||||
unimplemented("core:os procedure not supported on JS target")
|
||||
}
|
||||
|
||||
@(private)
|
||||
@(private, require_results)
|
||||
fix_long_path :: proc(path: string) -> string {
|
||||
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")
|
||||
}
|
||||
|
||||
unlink :: proc(path: string) -> (err: Errno) {
|
||||
unlink :: proc(path: string) -> (err: Error) {
|
||||
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")
|
||||
}
|
||||
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
|
||||
remove :: proc(name: string) -> Errno {
|
||||
remove :: proc(name: string) -> Error {
|
||||
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")
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
Handle :: distinct uintptr
|
||||
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)
|
||||
@@ -182,37 +225,34 @@ O_ASYNC :: 0x02000
|
||||
O_CLOEXEC :: 0x80000
|
||||
|
||||
|
||||
ERROR_NONE: Errno : 0
|
||||
ERROR_FILE_NOT_FOUND: Errno : 2
|
||||
ERROR_PATH_NOT_FOUND: Errno : 3
|
||||
ERROR_ACCESS_DENIED: Errno : 5
|
||||
ERROR_INVALID_HANDLE: Errno : 6
|
||||
ERROR_NOT_ENOUGH_MEMORY: Errno : 8
|
||||
ERROR_NO_MORE_FILES: Errno : 18
|
||||
ERROR_HANDLE_EOF: Errno : 38
|
||||
ERROR_NETNAME_DELETED: Errno : 64
|
||||
ERROR_FILE_EXISTS: Errno : 80
|
||||
ERROR_INVALID_PARAMETER: Errno : 87
|
||||
ERROR_BROKEN_PIPE: Errno : 109
|
||||
ERROR_BUFFER_OVERFLOW: Errno : 111
|
||||
ERROR_INSUFFICIENT_BUFFER: Errno : 122
|
||||
ERROR_MOD_NOT_FOUND: Errno : 126
|
||||
ERROR_PROC_NOT_FOUND: Errno : 127
|
||||
ERROR_DIR_NOT_EMPTY: Errno : 145
|
||||
ERROR_ALREADY_EXISTS: Errno : 183
|
||||
ERROR_ENVVAR_NOT_FOUND: Errno : 203
|
||||
ERROR_MORE_DATA: Errno : 234
|
||||
ERROR_OPERATION_ABORTED: Errno : 995
|
||||
ERROR_IO_PENDING: Errno : 997
|
||||
ERROR_NOT_FOUND: Errno : 1168
|
||||
ERROR_PRIVILEGE_NOT_HELD: Errno : 1314
|
||||
WSAEACCES: Errno : 10013
|
||||
WSAECONNRESET: Errno : 10054
|
||||
ERROR_FILE_NOT_FOUND :: Platform_Error.FILE_NOT_FOUND
|
||||
ERROR_PATH_NOT_FOUND :: Platform_Error.PATH_NOT_FOUND
|
||||
ERROR_ACCESS_DENIED :: Platform_Error.ACCESS_DENIED
|
||||
ERROR_INVALID_HANDLE :: Platform_Error.INVALID_HANDLE
|
||||
ERROR_NOT_ENOUGH_MEMORY :: Platform_Error.NOT_ENOUGH_MEMORY
|
||||
ERROR_NO_MORE_FILES :: Platform_Error.NO_MORE_FILES
|
||||
ERROR_HANDLE_EOF :: Platform_Error.HANDLE_EOF
|
||||
ERROR_NETNAME_DELETED :: Platform_Error.NETNAME_DELETED
|
||||
ERROR_FILE_EXISTS :: Platform_Error.FILE_EXISTS
|
||||
ERROR_INVALID_PARAMETER :: Platform_Error.INVALID_PARAMETER
|
||||
ERROR_BROKEN_PIPE :: Platform_Error.BROKEN_PIPE
|
||||
ERROR_BUFFER_OVERFLOW :: Platform_Error.BUFFER_OVERFLOW
|
||||
ERROR_INSUFFICIENT_BUFFER :: Platform_Error.INSUFFICIENT_BUFFER
|
||||
ERROR_MOD_NOT_FOUND :: Platform_Error.MOD_NOT_FOUND
|
||||
ERROR_PROC_NOT_FOUND :: Platform_Error.PROC_NOT_FOUND
|
||||
ERROR_DIR_NOT_EMPTY :: Platform_Error.DIR_NOT_EMPTY
|
||||
ERROR_ALREADY_EXISTS :: Platform_Error.ALREADY_EXISTS
|
||||
ERROR_ENVVAR_NOT_FOUND :: Platform_Error.ENVVAR_NOT_FOUND
|
||||
ERROR_MORE_DATA :: Platform_Error.MORE_DATA
|
||||
ERROR_OPERATION_ABORTED :: Platform_Error.OPERATION_ABORTED
|
||||
ERROR_IO_PENDING :: Platform_Error.IO_PENDING
|
||||
ERROR_NOT_FOUND :: Platform_Error.NOT_FOUND
|
||||
ERROR_PRIVILEGE_NOT_HELD :: Platform_Error.PRIVILEGE_NOT_HELD
|
||||
WSAEACCES :: Platform_Error.WSAEACCES
|
||||
WSAECONNRESET :: Platform_Error.WSAECONNRESET
|
||||
|
||||
// Windows reserves errors >= 1<<29 for application use
|
||||
ERROR_FILE_IS_PIPE: Errno : 1<<29 + 0
|
||||
ERROR_FILE_IS_NOT_DIR: Errno : 1<<29 + 1
|
||||
ERROR_NEGATIVE_OFFSET: Errno : 1<<29 + 2
|
||||
ERROR_FILE_IS_PIPE :: General_Error.File_Is_Pipe
|
||||
ERROR_FILE_IS_NOT_DIR :: General_Error.Not_Dir
|
||||
|
||||
// "Argv" arguments converted to Odin strings
|
||||
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")
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
get_page_size :: proc() -> int {
|
||||
unimplemented("core:os procedure not supported on JS target")
|
||||
}
|
||||
|
||||
@(private)
|
||||
@(private, require_results)
|
||||
_processor_core_count :: proc() -> int {
|
||||
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 {
|
||||
context = runtime.default_context()
|
||||
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 {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -20,148 +20,148 @@ import "base:intrinsics"
|
||||
// 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.
|
||||
import unix "core:sys/unix"
|
||||
import linux "core:sys/linux"
|
||||
|
||||
Handle :: distinct i32
|
||||
Pid :: distinct i32
|
||||
File_Time :: distinct u64
|
||||
Errno :: distinct i32
|
||||
Socket :: distinct int
|
||||
|
||||
INVALID_HANDLE :: ~Handle(0)
|
||||
|
||||
ERROR_NONE: Errno : 0
|
||||
EPERM: Errno : 1
|
||||
ENOENT: Errno : 2
|
||||
ESRCH: Errno : 3
|
||||
EINTR: Errno : 4
|
||||
EIO: Errno : 5
|
||||
ENXIO: Errno : 6
|
||||
EBADF: Errno : 9
|
||||
EAGAIN: Errno : 11
|
||||
ENOMEM: Errno : 12
|
||||
EACCES: Errno : 13
|
||||
EFAULT: Errno : 14
|
||||
EEXIST: Errno : 17
|
||||
ENODEV: Errno : 19
|
||||
ENOTDIR: Errno : 20
|
||||
EISDIR: Errno : 21
|
||||
EINVAL: Errno : 22
|
||||
ENFILE: Errno : 23
|
||||
EMFILE: Errno : 24
|
||||
ETXTBSY: Errno : 26
|
||||
EFBIG: Errno : 27
|
||||
ENOSPC: Errno : 28
|
||||
ESPIPE: Errno : 29
|
||||
EROFS: Errno : 30
|
||||
EPIPE: Errno : 32
|
||||
_Platform_Error :: linux.Errno
|
||||
EPERM :: Platform_Error.EPERM
|
||||
ENOENT :: Platform_Error.ENOENT
|
||||
ESRCH :: Platform_Error.ESRCH
|
||||
EINTR :: Platform_Error.EINTR
|
||||
EIO :: Platform_Error.EIO
|
||||
ENXIO :: Platform_Error.ENXIO
|
||||
EBADF :: Platform_Error.EBADF
|
||||
EAGAIN :: Platform_Error.EAGAIN
|
||||
ENOMEM :: Platform_Error.ENOMEM
|
||||
EACCES :: Platform_Error.EACCES
|
||||
EFAULT :: Platform_Error.EFAULT
|
||||
EEXIST :: Platform_Error.EEXIST
|
||||
ENODEV :: Platform_Error.ENODEV
|
||||
ENOTDIR :: Platform_Error.ENOTDIR
|
||||
EISDIR :: Platform_Error.EISDIR
|
||||
EINVAL :: Platform_Error.EINVAL
|
||||
ENFILE :: Platform_Error.ENFILE
|
||||
EMFILE :: Platform_Error.EMFILE
|
||||
ETXTBSY :: Platform_Error.ETXTBSY
|
||||
EFBIG :: Platform_Error.EFBIG
|
||||
ENOSPC :: Platform_Error.ENOSPC
|
||||
ESPIPE :: Platform_Error.ESPIPE
|
||||
EROFS :: Platform_Error.EROFS
|
||||
EPIPE :: Platform_Error.EPIPE
|
||||
|
||||
ERANGE: Errno : 34 /* Result too large */
|
||||
EDEADLK: Errno : 35 /* Resource deadlock would occur */
|
||||
ENAMETOOLONG: Errno : 36 /* File name too long */
|
||||
ENOLCK: Errno : 37 /* No record locks available */
|
||||
ERANGE :: Platform_Error.ERANGE /* Result too large */
|
||||
EDEADLK :: Platform_Error.EDEADLK /* Resource deadlock would occur */
|
||||
ENAMETOOLONG :: Platform_Error.ENAMETOOLONG /* File name too long */
|
||||
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 */
|
||||
ELOOP: Errno : 40 /* Too many symbolic links encountered */
|
||||
EWOULDBLOCK: Errno : EAGAIN /* Operation would block */
|
||||
ENOMSG: Errno : 42 /* No message of desired type */
|
||||
EIDRM: Errno : 43 /* Identifier removed */
|
||||
ECHRNG: Errno : 44 /* Channel number out of range */
|
||||
EL2NSYNC: Errno : 45 /* Level 2 not synchronized */
|
||||
EL3HLT: Errno : 46 /* Level 3 halted */
|
||||
EL3RST: Errno : 47 /* Level 3 reset */
|
||||
ELNRNG: Errno : 48 /* Link number out of range */
|
||||
EUNATCH: Errno : 49 /* Protocol driver not attached */
|
||||
ENOCSI: Errno : 50 /* No CSI structure available */
|
||||
EL2HLT: Errno : 51 /* Level 2 halted */
|
||||
EBADE: Errno : 52 /* Invalid exchange */
|
||||
EBADR: Errno : 53 /* Invalid request descriptor */
|
||||
EXFULL: Errno : 54 /* Exchange full */
|
||||
ENOANO: Errno : 55 /* No anode */
|
||||
EBADRQC: Errno : 56 /* Invalid request code */
|
||||
EBADSLT: Errno : 57 /* Invalid slot */
|
||||
EDEADLOCK: Errno : EDEADLK
|
||||
EBFONT: Errno : 59 /* Bad font file format */
|
||||
ENOSTR: Errno : 60 /* Device not a stream */
|
||||
ENODATA: Errno : 61 /* No data available */
|
||||
ETIME: Errno : 62 /* Timer expired */
|
||||
ENOSR: Errno : 63 /* Out of streams resources */
|
||||
ENONET: Errno : 64 /* Machine is not on the network */
|
||||
ENOPKG: Errno : 65 /* Package not installed */
|
||||
EREMOTE: Errno : 66 /* Object is remote */
|
||||
ENOLINK: Errno : 67 /* Link has been severed */
|
||||
EADV: Errno : 68 /* Advertise error */
|
||||
ESRMNT: Errno : 69 /* Srmount error */
|
||||
ECOMM: Errno : 70 /* Communication error on send */
|
||||
EPROTO: Errno : 71 /* Protocol error */
|
||||
EMULTIHOP: Errno : 72 /* Multihop attempted */
|
||||
EDOTDOT: Errno : 73 /* RFS specific error */
|
||||
EBADMSG: Errno : 74 /* Not a data message */
|
||||
EOVERFLOW: Errno : 75 /* Value too large for defined data type */
|
||||
ENOTUNIQ: Errno : 76 /* Name not unique on network */
|
||||
EBADFD: Errno : 77 /* File descriptor in bad state */
|
||||
EREMCHG: Errno : 78 /* Remote address changed */
|
||||
ELIBACC: Errno : 79 /* Can not access a needed shared library */
|
||||
ELIBBAD: Errno : 80 /* Accessing a corrupted shared library */
|
||||
ELIBSCN: Errno : 81 /* .lib section in a.out corrupted */
|
||||
ELIBMAX: Errno : 82 /* Attempting to link in too many shared libraries */
|
||||
ELIBEXEC: Errno : 83 /* Cannot exec a shared library directly */
|
||||
EILSEQ: Errno : 84 /* Illegal byte sequence */
|
||||
ERESTART: Errno : 85 /* Interrupted system call should be restarted */
|
||||
ESTRPIPE: Errno : 86 /* Streams pipe error */
|
||||
EUSERS: Errno : 87 /* Too many users */
|
||||
ENOTSOCK: Errno : 88 /* Socket operation on non-socket */
|
||||
EDESTADDRREQ: Errno : 89 /* Destination address required */
|
||||
EMSGSIZE: Errno : 90 /* Message too long */
|
||||
EPROTOTYPE: Errno : 91 /* Protocol wrong type for socket */
|
||||
ENOPROTOOPT: Errno : 92 /* Protocol not available */
|
||||
EPROTONOSUPPORT:Errno : 93 /* Protocol not supported */
|
||||
ESOCKTNOSUPPORT:Errno : 94 /* Socket type not supported */
|
||||
EOPNOTSUPP: Errno : 95 /* Operation not supported on transport endpoint */
|
||||
EPFNOSUPPORT: Errno : 96 /* Protocol family not supported */
|
||||
EAFNOSUPPORT: Errno : 97 /* Address family not supported by protocol */
|
||||
EADDRINUSE: Errno : 98 /* Address already in use */
|
||||
EADDRNOTAVAIL: Errno : 99 /* Cannot assign requested address */
|
||||
ENETDOWN: Errno : 100 /* Network is down */
|
||||
ENETUNREACH: Errno : 101 /* Network is unreachable */
|
||||
ENETRESET: Errno : 102 /* Network dropped connection because of reset */
|
||||
ECONNABORTED: Errno : 103 /* Software caused connection abort */
|
||||
ECONNRESET: Errno : 104 /* Connection reset by peer */
|
||||
ENOBUFS: Errno : 105 /* No buffer space available */
|
||||
EISCONN: Errno : 106 /* Transport endpoint is already connected */
|
||||
ENOTCONN: Errno : 107 /* Transport endpoint is not connected */
|
||||
ESHUTDOWN: Errno : 108 /* Cannot send after transport endpoint shutdown */
|
||||
ETOOMANYREFS: Errno : 109 /* Too many references: cannot splice */
|
||||
ETIMEDOUT: Errno : 110 /* Connection timed out */
|
||||
ECONNREFUSED: Errno : 111 /* Connection refused */
|
||||
EHOSTDOWN: Errno : 112 /* Host is down */
|
||||
EHOSTUNREACH: Errno : 113 /* No route to host */
|
||||
EALREADY: Errno : 114 /* Operation already in progress */
|
||||
EINPROGRESS: Errno : 115 /* Operation now in progress */
|
||||
ESTALE: Errno : 116 /* Stale file handle */
|
||||
EUCLEAN: Errno : 117 /* Structure needs cleaning */
|
||||
ENOTNAM: Errno : 118 /* Not a XENIX named type file */
|
||||
ENAVAIL: Errno : 119 /* No XENIX semaphores available */
|
||||
EISNAM: Errno : 120 /* Is a named type file */
|
||||
EREMOTEIO: Errno : 121 /* Remote I/O error */
|
||||
EDQUOT: Errno : 122 /* Quota exceeded */
|
||||
ENOTEMPTY :: Platform_Error.ENOTEMPTY /* Directory not empty */
|
||||
ELOOP :: Platform_Error.ELOOP /* Too many symbolic links encountered */
|
||||
EWOULDBLOCK :: Platform_Error.EWOULDBLOCK /* Operation would block */
|
||||
ENOMSG :: Platform_Error.ENOMSG /* No message of desired type */
|
||||
EIDRM :: Platform_Error.EIDRM /* Identifier removed */
|
||||
ECHRNG :: Platform_Error.ECHRNG /* Channel number out of range */
|
||||
EL2NSYNC :: Platform_Error.EL2NSYNC /* Level 2 not synchronized */
|
||||
EL3HLT :: Platform_Error.EL3HLT /* Level 3 halted */
|
||||
EL3RST :: Platform_Error.EL3RST /* Level 3 reset */
|
||||
ELNRNG :: Platform_Error.ELNRNG /* Link number out of range */
|
||||
EUNATCH :: Platform_Error.EUNATCH /* Protocol driver not attached */
|
||||
ENOCSI :: Platform_Error.ENOCSI /* No CSI structure available */
|
||||
EL2HLT :: Platform_Error.EL2HLT /* Level 2 halted */
|
||||
EBADE :: Platform_Error.EBADE /* Invalid exchange */
|
||||
EBADR :: Platform_Error.EBADR /* Invalid request descriptor */
|
||||
EXFULL :: Platform_Error.EXFULL /* Exchange full */
|
||||
ENOANO :: Platform_Error.ENOANO /* No anode */
|
||||
EBADRQC :: Platform_Error.EBADRQC /* Invalid request code */
|
||||
EBADSLT :: Platform_Error.EBADSLT /* Invalid slot */
|
||||
EDEADLOCK :: Platform_Error.EDEADLOCK
|
||||
EBFONT :: Platform_Error.EBFONT /* Bad font file format */
|
||||
ENOSTR :: Platform_Error.ENOSTR /* Device not a stream */
|
||||
ENODATA :: Platform_Error.ENODATA /* No data available */
|
||||
ETIME :: Platform_Error.ETIME /* Timer expired */
|
||||
ENOSR :: Platform_Error.ENOSR /* Out of streams resources */
|
||||
ENONET :: Platform_Error.ENONET /* Machine is not on the network */
|
||||
ENOPKG :: Platform_Error.ENOPKG /* Package not installed */
|
||||
EREMOTE :: Platform_Error.EREMOTE /* Object is remote */
|
||||
ENOLINK :: Platform_Error.ENOLINK /* Link has been severed */
|
||||
EADV :: Platform_Error.EADV /* Advertise error */
|
||||
ESRMNT :: Platform_Error.ESRMNT /* Srmount error */
|
||||
ECOMM :: Platform_Error.ECOMM /* Communication error on send */
|
||||
EPROTO :: Platform_Error.EPROTO /* Protocol error */
|
||||
EMULTIHOP :: Platform_Error.EMULTIHOP /* Multihop attempted */
|
||||
EDOTDOT :: Platform_Error.EDOTDOT /* RFS specific error */
|
||||
EBADMSG :: Platform_Error.EBADMSG /* Not a data message */
|
||||
EOVERFLOW :: Platform_Error.EOVERFLOW /* Value too large for defined data type */
|
||||
ENOTUNIQ :: Platform_Error.ENOTUNIQ /* Name not unique on network */
|
||||
EBADFD :: Platform_Error.EBADFD /* File descriptor in bad state */
|
||||
EREMCHG :: Platform_Error.EREMCHG /* Remote address changed */
|
||||
ELIBACC :: Platform_Error.ELIBACC /* Can not access a needed shared library */
|
||||
ELIBBAD :: Platform_Error.ELIBBAD /* Accessing a corrupted shared library */
|
||||
ELIBSCN :: Platform_Error.ELIBSCN /* .lib section in a.out corrupted */
|
||||
ELIBMAX :: Platform_Error.ELIBMAX /* Attempting to link in too many shared libraries */
|
||||
ELIBEXEC :: Platform_Error.ELIBEXEC /* Cannot exec a shared library directly */
|
||||
EILSEQ :: Platform_Error.EILSEQ /* Illegal byte sequence */
|
||||
ERESTART :: Platform_Error.ERESTART /* Interrupted system call should be restarted */
|
||||
ESTRPIPE :: Platform_Error.ESTRPIPE /* Streams pipe error */
|
||||
EUSERS :: Platform_Error.EUSERS /* Too many users */
|
||||
ENOTSOCK :: Platform_Error.ENOTSOCK /* Socket operation on non-socket */
|
||||
EDESTADDRREQ :: Platform_Error.EDESTADDRREQ /* Destination address required */
|
||||
EMSGSIZE :: Platform_Error.EMSGSIZE /* Message too long */
|
||||
EPROTOTYPE :: Platform_Error.EPROTOTYPE /* Protocol wrong type for socket */
|
||||
ENOPROTOOPT :: Platform_Error.ENOPROTOOPT /* Protocol not available */
|
||||
EPROTONOSUPPOR :: Platform_Error.EPROTONOSUPPORT /* Protocol not supported */
|
||||
ESOCKTNOSUPPOR :: Platform_Error.ESOCKTNOSUPPORT /* Socket type not supported */
|
||||
EOPNOTSUPP :: Platform_Error.EOPNOTSUPP /* Operation not supported on transport endpoint */
|
||||
EPFNOSUPPORT :: Platform_Error.EPFNOSUPPORT /* Protocol family not supported */
|
||||
EAFNOSUPPORT :: Platform_Error.EAFNOSUPPORT /* Address family not supported by protocol */
|
||||
EADDRINUSE :: Platform_Error.EADDRINUSE /* Address already in use */
|
||||
EADDRNOTAVAIL :: Platform_Error.EADDRNOTAVAIL /* Cannot assign requested address */
|
||||
ENETDOWN :: Platform_Error.ENETDOWN /* Network is down */
|
||||
ENETUNREACH :: Platform_Error.ENETUNREACH /* Network is unreachable */
|
||||
ENETRESET :: Platform_Error.ENETRESET /* Network dropped connection because of reset */
|
||||
ECONNABORTED :: Platform_Error.ECONNABORTED /* Software caused connection abort */
|
||||
ECONNRESET :: Platform_Error.ECONNRESET /* Connection reset by peer */
|
||||
ENOBUFS :: Platform_Error.ENOBUFS /* No buffer space available */
|
||||
EISCONN :: Platform_Error.EISCONN /* Transport endpoint is already connected */
|
||||
ENOTCONN :: Platform_Error.ENOTCONN /* Transport endpoint is not connected */
|
||||
ESHUTDOWN :: Platform_Error.ESHUTDOWN /* Cannot send after transport endpoint shutdown */
|
||||
ETOOMANYREFS :: Platform_Error.ETOOMANYREFS /* Too many references: cannot splice */
|
||||
ETIMEDOUT :: Platform_Error.ETIMEDOUT /* Connection timed out */
|
||||
ECONNREFUSED :: Platform_Error.ECONNREFUSED /* Connection refused */
|
||||
EHOSTDOWN :: Platform_Error.EHOSTDOWN /* Host is down */
|
||||
EHOSTUNREACH :: Platform_Error.EHOSTUNREACH /* No route to host */
|
||||
EALREADY :: Platform_Error.EALREADY /* Operation already in progress */
|
||||
EINPROGRESS :: Platform_Error.EINPROGRESS /* Operation now in progress */
|
||||
ESTALE :: Platform_Error.ESTALE /* Stale file handle */
|
||||
EUCLEAN :: Platform_Error.EUCLEAN /* Structure needs cleaning */
|
||||
ENOTNAM :: Platform_Error.ENOTNAM /* Not a XENIX named type file */
|
||||
ENAVAIL :: Platform_Error.ENAVAIL /* No XENIX semaphores available */
|
||||
EISNAM :: Platform_Error.EISNAM /* Is a named type file */
|
||||
EREMOTEIO :: Platform_Error.EREMOTEIO /* Remote I/O error */
|
||||
EDQUOT :: Platform_Error.EDQUOT /* Quota exceeded */
|
||||
|
||||
ENOMEDIUM: Errno : 123 /* No medium found */
|
||||
EMEDIUMTYPE: Errno : 124 /* Wrong medium type */
|
||||
ECANCELED: Errno : 125 /* Operation Canceled */
|
||||
ENOKEY: Errno : 126 /* Required key not available */
|
||||
EKEYEXPIRED: Errno : 127 /* Key has expired */
|
||||
EKEYREVOKED: Errno : 128 /* Key has been revoked */
|
||||
EKEYREJECTED: Errno : 129 /* Key was rejected by service */
|
||||
ENOMEDIUM :: Platform_Error.ENOMEDIUM /* No medium found */
|
||||
EMEDIUMTYPE :: Platform_Error.EMEDIUMTYPE /* Wrong medium type */
|
||||
ECANCELED :: Platform_Error.ECANCELED /* Operation Canceled */
|
||||
ENOKEY :: Platform_Error.ENOKEY /* Required key not available */
|
||||
EKEYEXPIRED :: Platform_Error.EKEYEXPIRED /* Key has expired */
|
||||
EKEYREVOKED :: Platform_Error.EKEYREVOKED /* Key has been revoked */
|
||||
EKEYREJECTED :: Platform_Error.EKEYREJECTED /* Key was rejected by service */
|
||||
|
||||
/* for robust mutexes */
|
||||
EOWNERDEAD: Errno : 130 /* Owner died */
|
||||
ENOTRECOVERABLE: Errno : 131 /* State not recoverable */
|
||||
EOWNERDEAD :: Platform_Error.EOWNERDEAD /* Owner died */
|
||||
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
|
||||
|
||||
@@ -448,13 +448,13 @@ S_ISGID :: 0o2000 // Set group id on execution
|
||||
S_ISVTX :: 0o1000 // Directory restrcted delete
|
||||
|
||||
|
||||
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 }
|
||||
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 }
|
||||
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 }
|
||||
S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK }
|
||||
@(require_results) S_ISLNK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFLNK }
|
||||
@(require_results) S_ISREG :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFREG }
|
||||
@(require_results) S_ISDIR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFDIR }
|
||||
@(require_results) S_ISCHR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFCHR }
|
||||
@(require_results) S_ISBLK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFBLK }
|
||||
@(require_results) S_ISFIFO :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFIFO }
|
||||
@(require_results) S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK }
|
||||
|
||||
F_OK :: 0 // Test for file existance
|
||||
X_OK :: 1 // Test for execute permission
|
||||
@@ -506,41 +506,55 @@ foreign dl {
|
||||
@(link_name="freeifaddrs") _freeifaddrs :: proc(ifa: ^ifaddrs) ---
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
is_path_separator :: proc(r: rune) -> bool {
|
||||
return r == '/'
|
||||
}
|
||||
|
||||
// determine errno from syscall return value
|
||||
@private
|
||||
_get_errno :: proc(res: int) -> Errno {
|
||||
@(private, require_results)
|
||||
_get_errno :: proc(res: int) -> Error {
|
||||
if res < 0 && res > -4096 {
|
||||
return Errno(-res)
|
||||
return Platform_Error(-res)
|
||||
}
|
||||
return 0
|
||||
return nil
|
||||
}
|
||||
|
||||
// get errno from libc
|
||||
get_last_error :: proc "contextless" () -> int {
|
||||
return int(__errno_location()^)
|
||||
@(require_results, no_instrumentation)
|
||||
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)
|
||||
if res == -1 {
|
||||
return _get_errno(res)
|
||||
}
|
||||
return ERROR_NONE
|
||||
return nil
|
||||
}
|
||||
|
||||
fork :: proc() -> (Pid, Errno) {
|
||||
@(require_results)
|
||||
fork :: proc() -> (Pid, Error) {
|
||||
pid := unix.sys_fork()
|
||||
if pid == -1 {
|
||||
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()
|
||||
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))
|
||||
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()
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
handle := unix.sys_open(cstr, flags, uint(mode))
|
||||
if handle < 0 {
|
||||
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)))
|
||||
}
|
||||
|
||||
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).
|
||||
// `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,
|
||||
@@ -578,9 +598,9 @@ close :: proc(fd: Handle) -> Errno {
|
||||
@(private)
|
||||
MAX_RW :: 1 << 30
|
||||
|
||||
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
read :: proc(fd: Handle, data: []byte) -> (int, Error) {
|
||||
if len(data) == 0 {
|
||||
return 0, ERROR_NONE
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
to_read := min(uint(len(data)), MAX_RW)
|
||||
@@ -589,12 +609,12 @@ read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
if bytes_read < 0 {
|
||||
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 {
|
||||
return 0, ERROR_NONE
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
to_write := min(uint(len(data)), MAX_RW)
|
||||
@@ -603,12 +623,12 @@ write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
if bytes_written < 0 {
|
||||
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 {
|
||||
return 0, ERROR_NONE
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
return 0, ERROR_NONE
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
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 {
|
||||
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)
|
||||
if res < 0 {
|
||||
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
|
||||
s: OS_Stat = ---
|
||||
result := unix.sys_fstat(int(fd), rawptr(&s))
|
||||
if result < 0 {
|
||||
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()
|
||||
old_path_cstr := strings.clone_to_cstring(old_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))
|
||||
}
|
||||
|
||||
remove :: proc(path: string) -> Errno {
|
||||
remove :: proc(path: string) -> Error {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
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()
|
||||
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
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()
|
||||
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
return _get_errno(unix.sys_rmdir(path_cstr))
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
is_file_handle :: proc(fd: Handle) -> bool {
|
||||
s, err := _fstat(fd)
|
||||
if err != ERROR_NONE {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return S_ISREG(s.mode)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
is_file_path :: proc(path: string, follow_links: bool = true) -> bool {
|
||||
s: OS_Stat
|
||||
err: Errno
|
||||
err: Error
|
||||
if follow_links {
|
||||
s, err = _stat(path)
|
||||
} else {
|
||||
s, err = _lstat(path)
|
||||
}
|
||||
if err != ERROR_NONE {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return S_ISREG(s.mode)
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
is_dir_handle :: proc(fd: Handle) -> bool {
|
||||
s, err := _fstat(fd)
|
||||
if err != ERROR_NONE {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return S_ISDIR(s.mode)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
|
||||
s: OS_Stat
|
||||
err: Errno
|
||||
err: Error
|
||||
if follow_links {
|
||||
s, err = _stat(path)
|
||||
} else {
|
||||
s, err = _lstat(path)
|
||||
}
|
||||
if err != ERROR_NONE {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
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_dir :: proc {is_dir_path, is_dir_handle}
|
||||
|
||||
@(require_results)
|
||||
exists :: proc(path: string) -> bool {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
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_by_name :: proc(name: string) -> File_Time {}
|
||||
*/
|
||||
last_write_time :: proc(fd: Handle) -> (File_Time, Errno) {
|
||||
s, err := _fstat(fd)
|
||||
if err != ERROR_NONE {
|
||||
return 0, err
|
||||
}
|
||||
@(require_results)
|
||||
last_write_time :: proc(fd: Handle) -> (time: File_Time, err: Error) {
|
||||
s := _fstat(fd) or_return
|
||||
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) {
|
||||
s, err := _stat(name)
|
||||
if err != ERROR_NONE {
|
||||
return 0, err
|
||||
}
|
||||
@(require_results)
|
||||
last_write_time_by_name :: proc(name: string) -> (time: File_Time, err: Error) {
|
||||
s := _stat(name) or_return
|
||||
modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
|
||||
return File_Time(modified), ERROR_NONE
|
||||
return File_Time(modified), nil
|
||||
}
|
||||
|
||||
@private
|
||||
_stat :: proc(path: string) -> (OS_Stat, Errno) {
|
||||
@(private, require_results)
|
||||
_stat :: proc(path: string) -> (OS_Stat, Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
|
||||
@@ -771,11 +793,11 @@ _stat :: proc(path: string) -> (OS_Stat, Errno) {
|
||||
if result < 0 {
|
||||
return s, _get_errno(result)
|
||||
}
|
||||
return s, ERROR_NONE
|
||||
return s, nil
|
||||
}
|
||||
|
||||
@private
|
||||
_lstat :: proc(path: string) -> (OS_Stat, Errno) {
|
||||
@(private, require_results)
|
||||
_lstat :: proc(path: string) -> (OS_Stat, Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
|
||||
@@ -785,53 +807,53 @@ _lstat :: proc(path: string) -> (OS_Stat, Errno) {
|
||||
if result < 0 {
|
||||
return s, _get_errno(result)
|
||||
}
|
||||
return s, ERROR_NONE
|
||||
return s, nil
|
||||
}
|
||||
|
||||
@private
|
||||
_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) {
|
||||
@(private, require_results)
|
||||
_fstat :: proc(fd: Handle) -> (OS_Stat, Error) {
|
||||
// deliberately uninitialized; the syscall fills this buffer for us
|
||||
s: OS_Stat = ---
|
||||
result := unix.sys_fstat(int(fd), rawptr(&s))
|
||||
if result < 0 {
|
||||
return s, _get_errno(result)
|
||||
}
|
||||
return s, ERROR_NONE
|
||||
return s, nil
|
||||
}
|
||||
|
||||
@private
|
||||
_fdopendir :: proc(fd: Handle) -> (Dir, Errno) {
|
||||
@(private, require_results)
|
||||
_fdopendir :: proc(fd: Handle) -> (Dir, Error) {
|
||||
dirp := _unix_fdopendir(fd)
|
||||
if dirp == cast(Dir)nil {
|
||||
return nil, Errno(get_last_error())
|
||||
return nil, get_last_error()
|
||||
}
|
||||
return dirp, ERROR_NONE
|
||||
return dirp, nil
|
||||
}
|
||||
|
||||
@private
|
||||
_closedir :: proc(dirp: Dir) -> Errno {
|
||||
@(private)
|
||||
_closedir :: proc(dirp: Dir) -> Error {
|
||||
rc := _unix_closedir(dirp)
|
||||
if rc != 0 {
|
||||
return Errno(get_last_error())
|
||||
return get_last_error()
|
||||
}
|
||||
return ERROR_NONE
|
||||
return nil
|
||||
}
|
||||
|
||||
@private
|
||||
@(private)
|
||||
_rewinddir :: proc(dirp: Dir) {
|
||||
_unix_rewinddir(dirp)
|
||||
}
|
||||
|
||||
@private
|
||||
_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) {
|
||||
@(private, require_results)
|
||||
_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Error, end_of_stream: bool) {
|
||||
result: ^Dirent
|
||||
rc := _unix_readdir_r(dirp, &entry, &result)
|
||||
|
||||
if rc != 0 {
|
||||
err = Errno(get_last_error())
|
||||
err = get_last_error()
|
||||
return
|
||||
}
|
||||
err = ERROR_NONE
|
||||
err = nil
|
||||
|
||||
if result == nil {
|
||||
end_of_stream = true
|
||||
@@ -842,8 +864,8 @@ _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool)
|
||||
return
|
||||
}
|
||||
|
||||
@private
|
||||
_readlink :: proc(path: string) -> (string, Errno) {
|
||||
@(private, require_results)
|
||||
_readlink :: proc(path: string) -> (string, Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
|
||||
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
|
||||
@@ -860,12 +882,13 @@ _readlink :: proc(path: string) -> (string, Errno) {
|
||||
delete(buf)
|
||||
buf = make([]byte, bufsz)
|
||||
} 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
|
||||
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)
|
||||
}
|
||||
|
||||
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
|
||||
if rel == "" {
|
||||
rel = "."
|
||||
@@ -886,25 +910,26 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
|
||||
|
||||
path_ptr := _unix_realpath(rel_cstr, nil)
|
||||
if path_ptr == nil {
|
||||
return "", Errno(get_last_error())
|
||||
return "", get_last_error()
|
||||
}
|
||||
defer _unix_free(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()
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
result := unix.sys_access(cstr, mask)
|
||||
if result < 0 {
|
||||
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) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == 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
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
|
||||
value, _ = lookup_env(key, allocator)
|
||||
return
|
||||
}
|
||||
|
||||
set_env :: proc(key, value: string) -> Errno {
|
||||
set_env :: proc(key, value: string) -> Error {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
key_cstring := strings.clone_to_cstring(key, 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
|
||||
res := _unix_setenv(key_cstring, value_cstring, 1)
|
||||
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()
|
||||
s := strings.clone_to_cstring(key, context.temp_allocator)
|
||||
res := _unix_putenv(s)
|
||||
if res < 0 {
|
||||
return Errno(get_last_error())
|
||||
return get_last_error()
|
||||
}
|
||||
return ERROR_NONE
|
||||
return nil
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
get_current_directory :: proc() -> string {
|
||||
// NOTE(tetra): I would use PATH_MAX here, but I was not able to find
|
||||
// an authoritative value for it across all systems.
|
||||
@@ -964,14 +991,14 @@ get_current_directory :: proc() -> string {
|
||||
unreachable()
|
||||
}
|
||||
|
||||
set_current_directory :: proc(path: string) -> (err: Errno) {
|
||||
set_current_directory :: proc(path: string) -> (err: Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
res := unix.sys_chdir(cstr)
|
||||
if res < 0 {
|
||||
return _get_errno(res)
|
||||
}
|
||||
return ERROR_NONE
|
||||
return nil
|
||||
}
|
||||
|
||||
exit :: proc "contextless" (code: int) -> ! {
|
||||
@@ -979,16 +1006,19 @@ exit :: proc "contextless" (code: int) -> ! {
|
||||
_unix_exit(c.int(code))
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
current_thread_id :: proc "contextless" () -> int {
|
||||
return unix.sys_gettid()
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
dlopen :: proc(filename: string, flags: int) -> rawptr {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
cstr := strings.clone_to_cstring(filename, context.temp_allocator)
|
||||
handle := _unix_dlopen(cstr, c.int(flags))
|
||||
return handle
|
||||
}
|
||||
@(require_results)
|
||||
dlsym :: proc(handle: rawptr, symbol: string) -> rawptr {
|
||||
assert(handle != nil)
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
@@ -1004,6 +1034,7 @@ dlerror :: proc() -> string {
|
||||
return string(_unix_dlerror())
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
get_page_size :: proc() -> int {
|
||||
// NOTE(tetra): The page size never changes, so why do anything complicated
|
||||
// if we don't have to.
|
||||
@@ -1016,11 +1047,12 @@ get_page_size :: proc() -> int {
|
||||
return page_size
|
||||
}
|
||||
|
||||
@(private)
|
||||
@(private, require_results)
|
||||
_processor_core_count :: proc() -> int {
|
||||
return int(_unix_get_nprocs())
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
_alloc_command_line_arguments :: proc() -> []string {
|
||||
res := make([]string, len(runtime.args__))
|
||||
for arg, i in runtime.args__ {
|
||||
@@ -1029,117 +1061,120 @@ _alloc_command_line_arguments :: proc() -> []string {
|
||||
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)
|
||||
if result < 0 {
|
||||
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)
|
||||
if result < 0 {
|
||||
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)
|
||||
if result < 0 {
|
||||
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)
|
||||
if result < 0 {
|
||||
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)
|
||||
if result < 0 {
|
||||
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)
|
||||
if result < 0 {
|
||||
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))
|
||||
if result < 0 {
|
||||
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)
|
||||
if result < 0 {
|
||||
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)
|
||||
if result < 0 {
|
||||
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)
|
||||
if result < 0 {
|
||||
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)
|
||||
if result < 0 {
|
||||
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)
|
||||
if result < 0 {
|
||||
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)
|
||||
if result < 0 {
|
||||
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))
|
||||
if result < 0 {
|
||||
return 0, _get_errno(result)
|
||||
}
|
||||
return result, ERROR_NONE
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -9,150 +9,289 @@ import "core:c"
|
||||
|
||||
Handle :: distinct i32
|
||||
File_Time :: distinct u64
|
||||
Errno :: distinct i32
|
||||
|
||||
INVALID_HANDLE :: ~Handle(0)
|
||||
|
||||
ERROR_NONE: Errno : 0 /* No error */
|
||||
EPERM: Errno : 1 /* Operation not permitted */
|
||||
ENOENT: Errno : 2 /* No such file or directory */
|
||||
EINTR: Errno : 4 /* Interrupted system call */
|
||||
ESRCH: Errno : 3 /* No such process */
|
||||
EIO: Errno : 5 /* Input/output error */
|
||||
ENXIO: Errno : 6 /* Device not configured */
|
||||
E2BIG: Errno : 7 /* Argument list too long */
|
||||
ENOEXEC: Errno : 8 /* Exec format error */
|
||||
EBADF: Errno : 9 /* Bad file descriptor */
|
||||
ECHILD: Errno : 10 /* No child processes */
|
||||
EDEADLK: Errno : 11 /* Resource deadlock avoided. 11 was EAGAIN */
|
||||
ENOMEM: Errno : 12 /* Cannot allocate memory */
|
||||
EACCES: Errno : 13 /* Permission denied */
|
||||
EFAULT: Errno : 14 /* Bad address */
|
||||
ENOTBLK: Errno : 15 /* Block device required */
|
||||
EBUSY: Errno : 16 /* Device busy */
|
||||
EEXIST: Errno : 17 /* File exists */
|
||||
EXDEV: Errno : 18 /* Cross-device link */
|
||||
ENODEV: Errno : 19 /* Operation not supported by device */
|
||||
ENOTDIR: Errno : 20 /* Not a directory */
|
||||
EISDIR: Errno : 21 /* Is a directory */
|
||||
EINVAL: Errno : 22 /* Invalid argument */
|
||||
ENFILE: Errno : 23 /* Too many open files in system */
|
||||
EMFILE: Errno : 24 /* Too many open files */
|
||||
ENOTTY: Errno : 25 /* Inappropriate ioctl for device */
|
||||
ETXTBSY: Errno : 26 /* Text file busy */
|
||||
EFBIG: Errno : 27 /* File too large */
|
||||
ENOSPC: Errno : 28 /* No space left on device */
|
||||
ESPIPE: Errno : 29 /* Illegal seek */
|
||||
EROFS: Errno : 30 /* Read-only file system */
|
||||
EMLINK: Errno : 31 /* Too many links */
|
||||
EPIPE: Errno : 32 /* Broken pipe */
|
||||
_Platform_Error :: enum i32 {
|
||||
NONE = 0,
|
||||
EPERM = 1, /* Operation not permitted */
|
||||
ENOENT = 2, /* No such file or directory */
|
||||
EINTR = 4, /* Interrupted system call */
|
||||
ESRCH = 3, /* No such process */
|
||||
EIO = 5, /* Input/output error */
|
||||
ENXIO = 6, /* Device not configured */
|
||||
E2BIG = 7, /* Argument list too long */
|
||||
ENOEXEC = 8, /* Exec format error */
|
||||
EBADF = 9, /* Bad file descriptor */
|
||||
ECHILD = 10, /* No child processes */
|
||||
EDEADLK = 11, /* Resource deadlock avoided. 11 was EAGAIN */
|
||||
ENOMEM = 12, /* Cannot allocate memory */
|
||||
EACCES = 13, /* Permission denied */
|
||||
EFAULT = 14, /* Bad address */
|
||||
ENOTBLK = 15, /* Block device required */
|
||||
EBUSY = 16, /* Device busy */
|
||||
EEXIST = 17, /* File exists */
|
||||
EXDEV = 18, /* Cross-device link */
|
||||
ENODEV = 19, /* Operation not supported by device */
|
||||
ENOTDIR = 20, /* Not a directory */
|
||||
EISDIR = 21, /* Is a directory */
|
||||
EINVAL = 22, /* Invalid argument */
|
||||
ENFILE = 23, /* Too many open files in system */
|
||||
EMFILE = 24, /* Too many open files */
|
||||
ENOTTY = 25, /* Inappropriate ioctl for device */
|
||||
ETXTBSY = 26, /* Text file busy */
|
||||
EFBIG = 27, /* File too large */
|
||||
ENOSPC = 28, /* No space left on device */
|
||||
ESPIPE = 29, /* Illegal seek */
|
||||
EROFS = 30, /* Read-only file system */
|
||||
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 */
|
||||
EDOM: Errno : 33 /* Numerical argument out of domain */
|
||||
ERANGE: Errno : 34 /* Result too large or too small */
|
||||
EDOM :: Platform_Error.EDOM /* Numerical argument out of domain */
|
||||
ERANGE :: Platform_Error.ERANGE /* Result too large or too small */
|
||||
|
||||
/* non-blocking and interrupt i/o */
|
||||
EAGAIN: Errno : 35 /* Resource temporarily unavailable */
|
||||
EWOULDBLOCK: Errno : EAGAIN /* Operation would block */
|
||||
EINPROGRESS: Errno : 36 /* Operation now in progress */
|
||||
EALREADY: Errno : 37 /* Operation already in progress */
|
||||
EAGAIN :: Platform_Error.EAGAIN /* Resource temporarily unavailable */
|
||||
EWOULDBLOCK :: EAGAIN /* Operation would block */
|
||||
EINPROGRESS :: Platform_Error.EINPROGRESS /* Operation now in progress */
|
||||
EALREADY :: Platform_Error.EALREADY /* Operation already in progress */
|
||||
|
||||
/* ipc/network software -- argument errors */
|
||||
ENOTSOCK: Errno : 38 /* Socket operation on non-socket */
|
||||
EDESTADDRREQ: Errno : 39 /* Destination address required */
|
||||
EMSGSIZE: Errno : 40 /* Message too long */
|
||||
EPROTOTYPE: Errno : 41 /* Protocol wrong type for socket */
|
||||
ENOPROTOOPT: Errno : 42 /* Protocol option not available */
|
||||
EPROTONOSUPPORT: Errno : 43 /* Protocol not supported */
|
||||
ESOCKTNOSUPPORT: Errno : 44 /* Socket type not supported */
|
||||
EOPNOTSUPP: Errno : 45 /* Operation not supported */
|
||||
EPFNOSUPPORT: Errno : 46 /* Protocol family not supported */
|
||||
EAFNOSUPPORT: Errno : 47 /* Address family not supported by protocol family */
|
||||
EADDRINUSE: Errno : 48 /* Address already in use */
|
||||
EADDRNOTAVAIL: Errno : 49 /* Can't assign requested address */
|
||||
ENOTSOCK :: Platform_Error.ENOTSOCK /* Socket operation on non-socket */
|
||||
EDESTADDRREQ :: Platform_Error.EDESTADDRREQ /* Destination address required */
|
||||
EMSGSIZE :: Platform_Error.EMSGSIZE /* Message too long */
|
||||
EPROTOTYPE :: Platform_Error.EPROTOTYPE /* Protocol wrong type for socket */
|
||||
ENOPROTOOPT :: Platform_Error.ENOPROTOOPT /* Protocol option not available */
|
||||
EPROTONOSUPPORT :: Platform_Error.EPROTONOSUPPORT /* Protocol not supported */
|
||||
ESOCKTNOSUPPORT :: Platform_Error.ESOCKTNOSUPPORT /* Socket type not supported */
|
||||
EOPNOTSUPP :: Platform_Error.EOPNOTSUPP /* Operation not supported */
|
||||
EPFNOSUPPORT :: Platform_Error.EPFNOSUPPORT /* Protocol family not supported */
|
||||
EAFNOSUPPORT :: Platform_Error.EAFNOSUPPORT /* Address family not supported by protocol family */
|
||||
EADDRINUSE :: Platform_Error.EADDRINUSE /* Address already in use */
|
||||
EADDRNOTAVAIL :: Platform_Error.EADDRNOTAVAIL /* Can't assign requested address */
|
||||
|
||||
/* ipc/network software -- operational errors */
|
||||
ENETDOWN: Errno : 50 /* Network is down */
|
||||
ENETUNREACH: Errno : 51 /* Network is unreachable */
|
||||
ENETRESET: Errno : 52 /* Network dropped connection on reset */
|
||||
ECONNABORTED: Errno : 53 /* Software caused connection abort */
|
||||
ECONNRESET: Errno : 54 /* Connection reset by peer */
|
||||
ENOBUFS: Errno : 55 /* No buffer space available */
|
||||
EISCONN: Errno : 56 /* Socket is already connected */
|
||||
ENOTCONN: Errno : 57 /* Socket is not connected */
|
||||
ESHUTDOWN: Errno : 58 /* Can't send after socket shutdown */
|
||||
ETOOMANYREFS: Errno : 59 /* Too many references: can't splice */
|
||||
ETIMEDOUT: Errno : 60 /* Operation timed out */
|
||||
ECONNREFUSED: Errno : 61 /* Connection refused */
|
||||
ENETDOWN :: Platform_Error.ENETDOWN /* Network is down */
|
||||
ENETUNREACH :: Platform_Error.ENETUNREACH /* Network is unreachable */
|
||||
ENETRESET :: Platform_Error.ENETRESET /* Network dropped connection on reset */
|
||||
ECONNABORTED :: Platform_Error.ECONNABORTED /* Software caused connection abort */
|
||||
ECONNRESET :: Platform_Error.ECONNRESET /* Connection reset by peer */
|
||||
ENOBUFS :: Platform_Error.ENOBUFS /* No buffer space available */
|
||||
EISCONN :: Platform_Error.EISCONN /* Socket is already connected */
|
||||
ENOTCONN :: Platform_Error.ENOTCONN /* Socket is not connected */
|
||||
ESHUTDOWN :: Platform_Error.ESHUTDOWN /* Can't send after socket shutdown */
|
||||
ETOOMANYREFS :: Platform_Error.ETOOMANYREFS /* Too many references: can't splice */
|
||||
ETIMEDOUT :: Platform_Error.ETIMEDOUT /* Operation timed out */
|
||||
ECONNREFUSED :: Platform_Error.ECONNREFUSED /* Connection refused */
|
||||
|
||||
ELOOP: Errno : 62 /* Too many levels of symbolic links */
|
||||
ENAMETOOLONG: Errno : 63 /* File name too long */
|
||||
ELOOP :: Platform_Error.ELOOP /* Too many levels of symbolic links */
|
||||
ENAMETOOLONG :: Platform_Error.ENAMETOOLONG /* File name too long */
|
||||
|
||||
/* should be rearranged */
|
||||
EHOSTDOWN: Errno : 64 /* Host is down */
|
||||
EHOSTUNREACH: Errno : 65 /* No route to host */
|
||||
ENOTEMPTY: Errno : 66 /* Directory not empty */
|
||||
EHOSTDOWN :: Platform_Error.EHOSTDOWN /* Host is down */
|
||||
EHOSTUNREACH :: Platform_Error.EHOSTUNREACH /* No route to host */
|
||||
ENOTEMPTY :: Platform_Error.ENOTEMPTY /* Directory not empty */
|
||||
|
||||
/* quotas & mush */
|
||||
EPROCLIM: Errno : 67 /* Too many processes */
|
||||
EUSERS: Errno : 68 /* Too many users */
|
||||
EDQUOT: Errno : 69 /* Disc quota exceeded */
|
||||
EPROCLIM :: Platform_Error.EPROCLIM /* Too many processes */
|
||||
EUSERS :: Platform_Error.EUSERS /* Too many users */
|
||||
EDQUOT :: Platform_Error.EDQUOT /* Disc quota exceeded */
|
||||
|
||||
/* Network File System */
|
||||
ESTALE: Errno : 70 /* Stale NFS file handle */
|
||||
EREMOTE: Errno : 71 /* Too many levels of remote in path */
|
||||
EBADRPC: Errno : 72 /* RPC struct is bad */
|
||||
ERPCMISMATCH: Errno : 73 /* RPC version wrong */
|
||||
EPROGUNAVAIL: Errno : 74 /* RPC prog. not avail */
|
||||
EPROGMISMATCH: Errno : 75 /* Program version wrong */
|
||||
EPROCUNAVAIL: Errno : 76 /* Bad procedure for program */
|
||||
ESTALE :: Platform_Error.ESTALE /* Stale NFS file handle */
|
||||
EREMOTE :: Platform_Error.EREMOTE /* Too many levels of remote in path */
|
||||
EBADRPC :: Platform_Error.EBADRPC /* RPC struct is bad */
|
||||
ERPCMISMATCH :: Platform_Error.ERPCMISMATCH /* RPC version wrong */
|
||||
EPROGUNAVAIL :: Platform_Error.EPROGUNAVAIL /* RPC prog. not avail */
|
||||
EPROGMISMATCH :: Platform_Error.EPROGMISMATCH /* Program version wrong */
|
||||
EPROCUNAVAIL :: Platform_Error.EPROCUNAVAIL /* Bad procedure for program */
|
||||
|
||||
ENOLCK: Errno : 77 /* No locks available */
|
||||
ENOSYS: Errno : 78 /* Function not implemented */
|
||||
ENOLCK :: Platform_Error.ENOLCK /* No locks available */
|
||||
ENOSYS :: Platform_Error.ENOSYS /* Function not implemented */
|
||||
|
||||
EFTYPE: Errno : 79 /* Inappropriate file type or format */
|
||||
EAUTH: Errno : 80 /* Authentication error */
|
||||
ENEEDAUTH: Errno : 81 /* Need authenticator */
|
||||
EFTYPE :: Platform_Error.EFTYPE /* Inappropriate file type or format */
|
||||
EAUTH :: Platform_Error.EAUTH /* Authentication error */
|
||||
ENEEDAUTH :: Platform_Error.ENEEDAUTH /* Need authenticator */
|
||||
|
||||
/* SystemV IPC */
|
||||
EIDRM: Errno : 82 /* Identifier removed */
|
||||
ENOMSG: Errno : 83 /* No message of desired type */
|
||||
EOVERFLOW: Errno : 84 /* Value too large to be stored in data type */
|
||||
EIDRM :: Platform_Error.EIDRM /* Identifier removed */
|
||||
ENOMSG :: Platform_Error.ENOMSG /* No message of desired type */
|
||||
EOVERFLOW :: Platform_Error.EOVERFLOW /* Value too large to be stored in data type */
|
||||
|
||||
/* 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 */
|
||||
/* Base, Realtime, Threads or Thread Priority Scheduling option errors */
|
||||
ENOTSUP: Errno : 86 /* Not supported */
|
||||
ENOTSUP :: Platform_Error.ENOTSUP /* Not supported */
|
||||
|
||||
/* Realtime option errors */
|
||||
ECANCELED: Errno : 87 /* Operation canceled */
|
||||
ECANCELED :: Platform_Error.ECANCELED /* Operation canceled */
|
||||
|
||||
/* Realtime, XSI STREAMS option errors */
|
||||
EBADMSG: Errno : 88 /* Bad or Corrupt message */
|
||||
EBADMSG :: Platform_Error.EBADMSG /* Bad or Corrupt message */
|
||||
|
||||
/* XSI STREAMS option errors */
|
||||
ENODATA: Errno : 89 /* No message available */
|
||||
ENOSR: Errno : 90 /* No STREAM resources */
|
||||
ENOSTR: Errno : 91 /* Not a STREAM */
|
||||
ETIME: Errno : 92 /* STREAM ioctl timeout */
|
||||
ENODATA :: Platform_Error.ENODATA /* No message available */
|
||||
ENOSR :: Platform_Error.ENOSR /* No STREAM resources */
|
||||
ENOSTR :: Platform_Error.ENOSTR /* Not a STREAM */
|
||||
ETIME :: Platform_Error.ETIME /* STREAM ioctl timeout */
|
||||
|
||||
/* File system extended attribute errors */
|
||||
ENOATTR: Errno : 93 /* Attribute not found */
|
||||
ENOATTR :: Platform_Error.ENOATTR /* Attribute not found */
|
||||
|
||||
/* Realtime, XSI STREAMS option errors */
|
||||
EMULTIHOP: Errno : 94 /* Multihop attempted */
|
||||
ENOLINK: Errno : 95 /* Link has been severed */
|
||||
EPROTO: Errno : 96 /* Protocol error */
|
||||
EMULTIHOP :: Platform_Error.EMULTIHOP /* Multihop attempted */
|
||||
ENOLINK :: Platform_Error.ENOLINK /* Link has been severed */
|
||||
EPROTO :: Platform_Error.EPROTO /* Protocol error */
|
||||
|
||||
/* Robust mutexes */
|
||||
EOWNERDEAD: Errno : 97 /* Previous owner died */
|
||||
ENOTRECOVERABLE: Errno : 98 /* State not recoverable */
|
||||
EOWNERDEAD :: Platform_Error.EOWNERDEAD /* Previous owner died */
|
||||
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_WRONLY :: 0x000000001
|
||||
@@ -268,13 +407,13 @@ S_ISUID :: 0o4000 // Set user id on execution
|
||||
S_ISGID :: 0o2000 // Set group id on execution
|
||||
S_ISVTX :: 0o1000 // Directory restrcted delete
|
||||
|
||||
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 }
|
||||
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 }
|
||||
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 }
|
||||
S_ISSOCK :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFSOCK }
|
||||
@(require_results) S_ISLNK :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFLNK }
|
||||
@(require_results) S_ISREG :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFREG }
|
||||
@(require_results) S_ISDIR :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFDIR }
|
||||
@(require_results) S_ISCHR :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFCHR }
|
||||
@(require_results) S_ISBLK :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFBLK }
|
||||
@(require_results) S_ISFIFO :: #force_inline proc "contextless" (m: mode_t) -> bool { return (m & S_IFMT) == S_IFIFO }
|
||||
@(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
|
||||
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.
|
||||
|
||||
@(require_results)
|
||||
is_path_separator :: proc(r: rune) -> bool {
|
||||
return r == '/'
|
||||
}
|
||||
|
||||
get_last_error :: proc "contextless" () -> int {
|
||||
return int(__errno_location()^)
|
||||
@(require_results, no_instrumentation)
|
||||
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()
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
handle := _unix_open(cstr, c.int(flags), c.int(mode))
|
||||
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)
|
||||
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.
|
||||
@(private)
|
||||
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)
|
||||
bytes_read := _unix_read(fd, &data[0], to_read)
|
||||
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 {
|
||||
return 0, ERROR_NONE
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
to_write := min(c.size_t(len(data)), MAX_RW)
|
||||
bytes_written := _unix_write(fd, &data[0], to_write)
|
||||
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))
|
||||
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) {
|
||||
s, err := _fstat(fd)
|
||||
if err != ERROR_NONE {
|
||||
return -1, err
|
||||
}
|
||||
return s.size, ERROR_NONE
|
||||
@(require_results)
|
||||
file_size :: proc(fd: Handle) -> (size: i64, err: Error) {
|
||||
size = -1
|
||||
s := _fstat(fd) or_return
|
||||
size = s.size
|
||||
return
|
||||
}
|
||||
|
||||
rename :: proc(old_path, new_path: string) -> Errno {
|
||||
rename :: proc(old_path, new_path: string) -> Error {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
old_path_cstr := strings.clone_to_cstring(old_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)
|
||||
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()
|
||||
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
res := _unix_unlink(path_cstr)
|
||||
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()
|
||||
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
res := _unix_mkdir(path_cstr, mode)
|
||||
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()
|
||||
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
res := _unix_rmdir(path_cstr)
|
||||
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 {
|
||||
s, err := _fstat(fd)
|
||||
if err != ERROR_NONE {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return S_ISREG(s.mode)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
is_file_path :: proc(path: string, follow_links: bool = true) -> bool {
|
||||
s: OS_Stat
|
||||
err: Errno
|
||||
err: Error
|
||||
if follow_links {
|
||||
s, err = _stat(path)
|
||||
} else {
|
||||
s, err = _lstat(path)
|
||||
}
|
||||
if err != ERROR_NONE {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return S_ISREG(s.mode)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
is_dir_handle :: proc(fd: Handle) -> bool {
|
||||
s, err := _fstat(fd)
|
||||
if err != ERROR_NONE {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return S_ISDIR(s.mode)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
|
||||
s: OS_Stat
|
||||
err: Errno
|
||||
err: Error
|
||||
if follow_links {
|
||||
s, err = _stat(path)
|
||||
} else {
|
||||
s, err = _lstat(path)
|
||||
}
|
||||
if err != ERROR_NONE {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
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_dir :: proc {is_dir_path, is_dir_handle}
|
||||
|
||||
@(require_results)
|
||||
exists :: proc(path: string) -> bool {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
cpath := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
@@ -497,12 +669,13 @@ exists :: proc(path: string) -> bool {
|
||||
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))
|
||||
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
|
||||
@@ -511,38 +684,34 @@ stdin: Handle = 0
|
||||
stdout: Handle = 1
|
||||
stderr: Handle = 2
|
||||
|
||||
last_write_time :: proc(fd: Handle) -> (File_Time, Errno) {
|
||||
s, err := _fstat(fd)
|
||||
if err != ERROR_NONE {
|
||||
return 0, err
|
||||
}
|
||||
@(require_results)
|
||||
last_write_time :: proc(fd: Handle) -> (time: File_Time, err: Error) {
|
||||
s := _fstat(fd) or_return
|
||||
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) {
|
||||
s, err := _stat(name)
|
||||
if err != ERROR_NONE {
|
||||
return 0, err
|
||||
}
|
||||
@(require_results)
|
||||
last_write_time_by_name :: proc(name: string) -> (time: File_Time, err: Error) {
|
||||
s := _stat(name) or_return
|
||||
modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
|
||||
return File_Time(modified), ERROR_NONE
|
||||
return File_Time(modified), nil
|
||||
}
|
||||
|
||||
@private
|
||||
_stat :: proc(path: string) -> (OS_Stat, Errno) {
|
||||
@(private, require_results)
|
||||
_stat :: proc(path: string) -> (OS_Stat, Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
s: OS_Stat = ---
|
||||
result := _unix_lstat(cstr, &s)
|
||||
if result == -1 {
|
||||
return s, Errno(get_last_error())
|
||||
return s, get_last_error()
|
||||
}
|
||||
return s, ERROR_NONE
|
||||
return s, nil
|
||||
}
|
||||
|
||||
@private
|
||||
_lstat :: proc(path: string) -> (OS_Stat, Errno) {
|
||||
@(private, require_results)
|
||||
_lstat :: proc(path: string) -> (OS_Stat, Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
|
||||
@@ -550,54 +719,54 @@ _lstat :: proc(path: string) -> (OS_Stat, Errno) {
|
||||
s: OS_Stat = ---
|
||||
res := _unix_lstat(cstr, &s)
|
||||
if res == -1 {
|
||||
return s, Errno(get_last_error())
|
||||
return s, get_last_error()
|
||||
}
|
||||
return s, ERROR_NONE
|
||||
return s, nil
|
||||
}
|
||||
|
||||
@private
|
||||
_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) {
|
||||
@(private, require_results)
|
||||
_fstat :: proc(fd: Handle) -> (OS_Stat, Error) {
|
||||
s: OS_Stat = ---
|
||||
result := _unix_fstat(fd, &s)
|
||||
if result == -1 {
|
||||
return s, Errno(get_last_error())
|
||||
return s, get_last_error()
|
||||
}
|
||||
return s, ERROR_NONE
|
||||
return s, nil
|
||||
}
|
||||
|
||||
@private
|
||||
_fdopendir :: proc(fd: Handle) -> (Dir, Errno) {
|
||||
@(private, require_results)
|
||||
_fdopendir :: proc(fd: Handle) -> (Dir, Error) {
|
||||
dirp := _unix_fdopendir(fd)
|
||||
if dirp == cast(Dir)nil {
|
||||
return nil, Errno(get_last_error())
|
||||
return nil, get_last_error()
|
||||
}
|
||||
return dirp, ERROR_NONE
|
||||
return dirp, nil
|
||||
}
|
||||
|
||||
@private
|
||||
_closedir :: proc(dirp: Dir) -> Errno {
|
||||
@(private)
|
||||
_closedir :: proc(dirp: Dir) -> Error {
|
||||
rc := _unix_closedir(dirp)
|
||||
if rc != 0 {
|
||||
return Errno(get_last_error())
|
||||
return get_last_error()
|
||||
}
|
||||
return ERROR_NONE
|
||||
return nil
|
||||
}
|
||||
|
||||
@private
|
||||
@(private)
|
||||
_rewinddir :: proc(dirp: Dir) {
|
||||
_unix_rewinddir(dirp)
|
||||
}
|
||||
|
||||
@private
|
||||
_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) {
|
||||
@(private, require_results)
|
||||
_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Error, end_of_stream: bool) {
|
||||
result: ^Dirent
|
||||
rc := _unix_readdir_r(dirp, &entry, &result)
|
||||
|
||||
if rc != 0 {
|
||||
err = Errno(get_last_error())
|
||||
err = get_last_error()
|
||||
return
|
||||
}
|
||||
err = ERROR_NONE
|
||||
err = nil
|
||||
|
||||
if result == nil {
|
||||
end_of_stream = true
|
||||
@@ -607,8 +776,8 @@ _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool)
|
||||
return
|
||||
}
|
||||
|
||||
@private
|
||||
_readlink :: proc(path: string) -> (string, Errno) {
|
||||
@(private, require_results)
|
||||
_readlink :: proc(path: string) -> (string, Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.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)
|
||||
if rc == -1 {
|
||||
delete(buf)
|
||||
return "", Errno(get_last_error())
|
||||
return "", get_last_error()
|
||||
} else if rc == int(bufsz) {
|
||||
bufsz += MAX_PATH
|
||||
delete(buf)
|
||||
buf = make([]byte, bufsz)
|
||||
} 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
|
||||
_, err := fcntl(int(fd), F_GETPATH, int(uintptr(&buf[0])))
|
||||
if err != ERROR_NONE {
|
||||
return "", err
|
||||
}
|
||||
|
||||
path := strings.clone_from_cstring(cstring(&buf[0]))
|
||||
return path, err
|
||||
_ = fcntl(int(fd), F_GETPATH, int(uintptr(&buf[0]))) or_return
|
||||
return strings.clone_from_cstring(cstring(&buf[0]))
|
||||
}
|
||||
|
||||
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
|
||||
if rel == "" {
|
||||
rel = "."
|
||||
@@ -654,26 +820,27 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
|
||||
|
||||
path_ptr := _unix_realpath(rel_cstr, nil)
|
||||
if path_ptr == nil {
|
||||
return "", Errno(get_last_error())
|
||||
return "", get_last_error()
|
||||
}
|
||||
defer _unix_free(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()
|
||||
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
result := _unix_access(cstr, c.int(mask))
|
||||
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) {
|
||||
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
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
|
||||
value, _ = lookup_env(key, allocator)
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
get_current_directory :: proc() -> string {
|
||||
// NOTE(tetra): I would use PATH_MAX here, but I was not able to find
|
||||
// an authoritative value for it across all systems.
|
||||
@@ -701,7 +870,7 @@ get_current_directory :: proc() -> string {
|
||||
if cwd != nil {
|
||||
return string(cwd)
|
||||
}
|
||||
if Errno(get_last_error()) != ERANGE {
|
||||
if get_last_error() != ERANGE {
|
||||
delete(buf)
|
||||
return ""
|
||||
}
|
||||
@@ -710,14 +879,14 @@ get_current_directory :: proc() -> string {
|
||||
unreachable()
|
||||
}
|
||||
|
||||
set_current_directory :: proc(path: string) -> (err: Errno) {
|
||||
set_current_directory :: proc(path: string) -> (err: Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
res := _unix_chdir(cstr)
|
||||
if res == -1 {
|
||||
return Errno(get_last_error())
|
||||
return get_last_error()
|
||||
}
|
||||
return ERROR_NONE
|
||||
return nil
|
||||
}
|
||||
|
||||
exit :: proc "contextless" (code: int) -> ! {
|
||||
@@ -725,10 +894,12 @@ exit :: proc "contextless" (code: int) -> ! {
|
||||
_unix_exit(c.int(code))
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
current_thread_id :: proc "contextless" () -> int {
|
||||
return int(_lwp_self())
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
dlopen :: proc(filename: string, flags: int) -> rawptr {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
cstr := strings.clone_to_cstring(filename, context.temp_allocator)
|
||||
@@ -736,6 +907,7 @@ dlopen :: proc(filename: string, flags: int) -> rawptr {
|
||||
return handle
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
dlsym :: proc(handle: rawptr, symbol: string) -> rawptr {
|
||||
assert(handle != nil)
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
@@ -749,10 +921,12 @@ dlclose :: proc(handle: rawptr) -> bool {
|
||||
return _unix_dlclose(handle) == 0
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
dlerror :: proc() -> string {
|
||||
return string(_unix_dlerror())
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
get_page_size :: proc() -> int {
|
||||
// NOTE(tetra): The page size never changes, so why do anything complicated
|
||||
// if we don't have to.
|
||||
@@ -765,7 +939,7 @@ get_page_size :: proc() -> int {
|
||||
return page_size
|
||||
}
|
||||
|
||||
@(private)
|
||||
@(private, require_results)
|
||||
_processor_core_count :: proc() -> int {
|
||||
count : int = 0
|
||||
count_size := size_of(count)
|
||||
@@ -778,6 +952,7 @@ _processor_core_count :: proc() -> int {
|
||||
return 1
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
_alloc_command_line_arguments :: proc() -> []string {
|
||||
res := make([]string, len(runtime.args__))
|
||||
for arg, i in runtime.args__ {
|
||||
|
||||
@@ -9,108 +9,205 @@ import "base:runtime"
|
||||
Handle :: distinct i32
|
||||
Pid :: distinct i32
|
||||
File_Time :: distinct u64
|
||||
Errno :: distinct i32
|
||||
|
||||
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
|
||||
ENOENT: Errno: 2
|
||||
ESRCH: Errno: 3
|
||||
EINTR: Errno: 4
|
||||
EIO: Errno: 5
|
||||
ENXIO: Errno: 6
|
||||
E2BIG: Errno: 7
|
||||
ENOEXEC: Errno: 8
|
||||
EBADF: Errno: 9
|
||||
ECHILD: Errno: 10
|
||||
EDEADLK: Errno: 11
|
||||
ENOMEM: Errno: 12
|
||||
EACCES: Errno: 13
|
||||
EFAULT: Errno: 14
|
||||
ENOTBLK: Errno: 15
|
||||
EBUSY: Errno: 16
|
||||
EEXIST: Errno: 17
|
||||
EXDEV: Errno: 18
|
||||
ENODEV: Errno: 19
|
||||
ENOTDIR: Errno: 20
|
||||
EISDIR: Errno: 21
|
||||
EINVAL: Errno: 22
|
||||
ENFILE: Errno: 23
|
||||
EMFILE: Errno: 24
|
||||
ENOTTY: Errno: 25
|
||||
ETXTBSY: Errno: 26
|
||||
EFBIG: Errno: 27
|
||||
ENOSPC: Errno: 28
|
||||
ESPIPE: Errno: 29
|
||||
EROFS: Errno: 30
|
||||
EMLINK: Errno: 31
|
||||
EPIPE: Errno: 32
|
||||
EDOM: Errno: 33
|
||||
ERANGE: Errno: 34
|
||||
EAGAIN: Errno: 35
|
||||
EWOULDBLOCK: Errno: EAGAIN
|
||||
EINPROGRESS: Errno: 36
|
||||
EALREADY: Errno: 37
|
||||
ENOTSOCK: Errno: 38
|
||||
EDESTADDRREQ: Errno: 39
|
||||
EMSGSIZE: Errno: 40
|
||||
EPROTOTYPE: Errno: 41
|
||||
ENOPROTOOPT: Errno: 42
|
||||
EPROTONOSUPPORT: Errno: 43
|
||||
ESOCKTNOSUPPORT: Errno: 44
|
||||
EOPNOTSUPP: Errno: 45
|
||||
EPFNOSUPPORT: Errno: 46
|
||||
EAFNOSUPPORT: Errno: 47
|
||||
EADDRINUSE: Errno: 48
|
||||
EADDRNOTAVAIL: Errno: 49
|
||||
ENETDOWN: Errno: 50
|
||||
ENETUNREACH: Errno: 51
|
||||
ENETRESET: Errno: 52
|
||||
ECONNABORTED: Errno: 53
|
||||
ECONNRESET: Errno: 54
|
||||
ENOBUFS: Errno: 55
|
||||
EISCONN: Errno: 56
|
||||
ENOTCONN: Errno: 57
|
||||
ESHUTDOWN: Errno: 58
|
||||
ETOOMANYREFS: Errno: 59
|
||||
ETIMEDOUT: Errno: 60
|
||||
ECONNREFUSED: Errno: 61
|
||||
ELOOP: Errno: 62
|
||||
ENAMETOOLONG: Errno: 63
|
||||
EHOSTDOWN: Errno: 64
|
||||
EHOSTUNREACH: Errno: 65
|
||||
ENOTEMPTY: Errno: 66
|
||||
EPROCLIM: Errno: 67
|
||||
EUSERS: Errno: 68
|
||||
EDQUOT: Errno: 69
|
||||
ESTALE: Errno: 70
|
||||
EREMOTE: Errno: 71
|
||||
EBADRPC: Errno: 72
|
||||
ERPCMISMATCH: Errno: 73
|
||||
EPROGUNAVAIL: Errno: 74
|
||||
EPROGMISMATCH: Errno: 75
|
||||
EPROCUNAVAIL: Errno: 76
|
||||
ENOLCK: Errno: 77
|
||||
ENOSYS: Errno: 78
|
||||
EFTYPE: Errno: 79
|
||||
EAUTH: Errno: 80
|
||||
ENEEDAUTH: Errno: 81
|
||||
EIPSEC: Errno: 82
|
||||
ENOATTR: Errno: 83
|
||||
EILSEQ: Errno: 84
|
||||
ENOMEDIUM: Errno: 85
|
||||
EMEDIUMTYPE: Errno: 86
|
||||
EOVERFLOW: Errno: 87
|
||||
ECANCELED: Errno: 88
|
||||
EIDRM: Errno: 89
|
||||
ENOMSG: Errno: 90
|
||||
ENOTSUP: Errno: 91
|
||||
EBADMSG: Errno: 92
|
||||
ENOTRECOVERABLE: Errno: 93
|
||||
EOWNERDEAD: Errno: 94
|
||||
EPROTO: Errno: 95
|
||||
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
|
||||
EDEADLK :: Platform_Error.EDEADLK
|
||||
ENOMEM :: Platform_Error.ENOMEM
|
||||
EACCES :: Platform_Error.EACCES
|
||||
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
|
||||
EWOULDBLOCK :: Platform_Error.EWOULDBLOCK
|
||||
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
|
||||
ETOOMANYREFS :: Platform_Error.ETOOMANYREFS
|
||||
ETIMEDOUT :: Platform_Error.ETIMEDOUT
|
||||
ECONNREFUSED :: Platform_Error.ECONNREFUSED
|
||||
ELOOP :: Platform_Error.ELOOP
|
||||
ENAMETOOLONG :: Platform_Error.ENAMETOOLONG
|
||||
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
|
||||
EREMOTE :: Platform_Error.EREMOTE
|
||||
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
|
||||
EIPSEC :: Platform_Error.EIPSEC
|
||||
ENOATTR :: Platform_Error.ENOATTR
|
||||
EILSEQ :: Platform_Error.EILSEQ
|
||||
ENOMEDIUM :: Platform_Error.ENOMEDIUM
|
||||
EMEDIUMTYPE :: Platform_Error.EMEDIUMTYPE
|
||||
EOVERFLOW :: Platform_Error.EOVERFLOW
|
||||
ECANCELED :: Platform_Error.ECANCELED
|
||||
EIDRM :: Platform_Error.EIDRM
|
||||
ENOMSG :: Platform_Error.ENOMSG
|
||||
ENOTSUP :: Platform_Error.ENOTSUP
|
||||
EBADMSG :: Platform_Error.EBADMSG
|
||||
ENOTRECOVERABLE :: Platform_Error.ENOTRECOVERABLE
|
||||
EOWNERDEAD :: Platform_Error.EOWNERDEAD
|
||||
EPROTO :: Platform_Error.EPROTO
|
||||
|
||||
O_RDONLY :: 0x00000
|
||||
O_WRONLY :: 0x00001
|
||||
@@ -225,13 +322,13 @@ S_ISUID :: 0o4000 // Set user id on execution
|
||||
S_ISGID :: 0o2000 // Set group id on execution
|
||||
S_ISTXT :: 0o1000 // Sticky bit
|
||||
|
||||
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 }
|
||||
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 }
|
||||
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 }
|
||||
S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK }
|
||||
@(require_results) S_ISLNK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFLNK }
|
||||
@(require_results) S_ISREG :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFREG }
|
||||
@(require_results) S_ISDIR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFDIR }
|
||||
@(require_results) S_ISCHR :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFCHR }
|
||||
@(require_results) S_ISBLK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFBLK }
|
||||
@(require_results) S_ISFIFO :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFIFO }
|
||||
@(require_results) S_ISSOCK :: #force_inline proc(m: u32) -> bool { return (m & S_IFMT) == S_IFSOCK }
|
||||
|
||||
F_OK :: 0x00 // Test for file existance
|
||||
X_OK :: 0x01 // Test for execute permission
|
||||
@@ -291,38 +388,47 @@ foreign libc {
|
||||
@(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
is_path_separator :: proc(r: rune) -> bool {
|
||||
return r == '/'
|
||||
}
|
||||
|
||||
get_last_error :: proc "contextless" () -> int {
|
||||
return int(__error()^)
|
||||
@(require_results, no_instrumentation)
|
||||
get_last_error :: proc "contextless" () -> Error {
|
||||
return Platform_Error(__error()^)
|
||||
}
|
||||
|
||||
fork :: proc() -> (Pid, Errno) {
|
||||
@(require_results)
|
||||
fork :: proc() -> (Pid, Error) {
|
||||
pid := _unix_fork()
|
||||
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()
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
handle := _unix_open(cstr, c.int(flags), c.int(mode))
|
||||
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)
|
||||
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`.
|
||||
@@ -333,124 +439,148 @@ close :: proc(fd: Handle) -> Errno {
|
||||
@(private)
|
||||
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)
|
||||
bytes_read := _unix_read(fd, &data[0], to_read)
|
||||
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 {
|
||||
return 0, ERROR_NONE
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
to_write := min(c.size_t(len(data)), MAX_RW)
|
||||
bytes_written := _unix_write(fd, &data[0], to_write)
|
||||
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))
|
||||
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) {
|
||||
s, err := _fstat(fd)
|
||||
if err != ERROR_NONE {
|
||||
return -1, err
|
||||
}
|
||||
return s.size, ERROR_NONE
|
||||
@(require_results)
|
||||
file_size :: proc(fd: Handle) -> (size: i64, err: Error) {
|
||||
size = -1
|
||||
s := _fstat(fd) or_return
|
||||
size = s.size
|
||||
return
|
||||
}
|
||||
|
||||
rename :: proc(old_path, new_path: string) -> Errno {
|
||||
rename :: proc(old_path, new_path: string) -> Error {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
old_path_cstr := strings.clone_to_cstring(old_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)
|
||||
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()
|
||||
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
res := _unix_unlink(path_cstr)
|
||||
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()
|
||||
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
res := _unix_mkdir(path_cstr, mode)
|
||||
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()
|
||||
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
res := _unix_rmdir(path_cstr)
|
||||
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 {
|
||||
s, err := _fstat(fd)
|
||||
if err != ERROR_NONE {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return S_ISREG(s.mode)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
is_file_path :: proc(path: string, follow_links: bool = true) -> bool {
|
||||
s: OS_Stat
|
||||
err: Errno
|
||||
err: Error
|
||||
if follow_links {
|
||||
s, err = _stat(path)
|
||||
} else {
|
||||
s, err = _lstat(path)
|
||||
}
|
||||
if err != ERROR_NONE {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return S_ISREG(s.mode)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
is_dir_handle :: proc(fd: Handle) -> bool {
|
||||
s, err := _fstat(fd)
|
||||
if err != ERROR_NONE {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return S_ISDIR(s.mode)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
is_dir_path :: proc(path: string, follow_links: bool = true) -> bool {
|
||||
s: OS_Stat
|
||||
err: Errno
|
||||
err: Error
|
||||
if follow_links {
|
||||
s, err = _stat(path)
|
||||
} else {
|
||||
s, err = _lstat(path)
|
||||
}
|
||||
if err != ERROR_NONE {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return S_ISDIR(s.mode)
|
||||
@@ -469,26 +599,22 @@ stderr: Handle = 2
|
||||
last_write_time :: proc(fd: Handle) -> File_Time {}
|
||||
last_write_time_by_name :: proc(name: string) -> File_Time {}
|
||||
*/
|
||||
last_write_time :: proc(fd: Handle) -> (File_Time, Errno) {
|
||||
s, err := _fstat(fd)
|
||||
if err != ERROR_NONE {
|
||||
return 0, err
|
||||
}
|
||||
@(require_results)
|
||||
last_write_time :: proc(fd: Handle) -> (time: File_Time, err: Error) {
|
||||
s := _fstat(fd) or_return
|
||||
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) {
|
||||
s, err := _stat(name)
|
||||
if err != ERROR_NONE {
|
||||
return 0, err
|
||||
}
|
||||
@(require_results)
|
||||
last_write_time_by_name :: proc(name: string) -> (time: File_Time, err: Error) {
|
||||
s := _stat(name) or_return
|
||||
modified := s.modified.seconds * 1_000_000_000 + s.modified.nanoseconds
|
||||
return File_Time(modified), ERROR_NONE
|
||||
return File_Time(modified), nil
|
||||
}
|
||||
|
||||
@private
|
||||
_stat :: proc(path: string) -> (OS_Stat, Errno) {
|
||||
@(private, require_results)
|
||||
_stat :: proc(path: string) -> (OS_Stat, Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
|
||||
@@ -496,13 +622,13 @@ _stat :: proc(path: string) -> (OS_Stat, Errno) {
|
||||
s: OS_Stat = ---
|
||||
res := _unix_stat(cstr, &s)
|
||||
if res == -1 {
|
||||
return s, Errno(get_last_error())
|
||||
return s, get_last_error()
|
||||
}
|
||||
return s, ERROR_NONE
|
||||
return s, nil
|
||||
}
|
||||
|
||||
@private
|
||||
_lstat :: proc(path: string) -> (OS_Stat, Errno) {
|
||||
@(private, require_results)
|
||||
_lstat :: proc(path: string) -> (OS_Stat, Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
|
||||
@@ -510,55 +636,55 @@ _lstat :: proc(path: string) -> (OS_Stat, Errno) {
|
||||
s: OS_Stat = ---
|
||||
res := _unix_lstat(cstr, &s)
|
||||
if res == -1 {
|
||||
return s, Errno(get_last_error())
|
||||
return s, get_last_error()
|
||||
}
|
||||
return s, ERROR_NONE
|
||||
return s, nil
|
||||
}
|
||||
|
||||
@private
|
||||
_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) {
|
||||
@(private, require_results)
|
||||
_fstat :: proc(fd: Handle) -> (OS_Stat, Error) {
|
||||
// deliberately uninitialized
|
||||
s: OS_Stat = ---
|
||||
res := _unix_fstat(fd, &s)
|
||||
if res == -1 {
|
||||
return s, Errno(get_last_error())
|
||||
return s, get_last_error()
|
||||
}
|
||||
return s, ERROR_NONE
|
||||
return s, nil
|
||||
}
|
||||
|
||||
@private
|
||||
_fdopendir :: proc(fd: Handle) -> (Dir, Errno) {
|
||||
@(private, require_results)
|
||||
_fdopendir :: proc(fd: Handle) -> (Dir, Error) {
|
||||
dirp := _unix_fdopendir(fd)
|
||||
if dirp == cast(Dir)nil {
|
||||
return nil, Errno(get_last_error())
|
||||
return nil, get_last_error()
|
||||
}
|
||||
return dirp, ERROR_NONE
|
||||
return dirp, nil
|
||||
}
|
||||
|
||||
@private
|
||||
_closedir :: proc(dirp: Dir) -> Errno {
|
||||
@(private)
|
||||
_closedir :: proc(dirp: Dir) -> Error {
|
||||
rc := _unix_closedir(dirp)
|
||||
if rc != 0 {
|
||||
return Errno(get_last_error())
|
||||
return get_last_error()
|
||||
}
|
||||
return ERROR_NONE
|
||||
return nil
|
||||
}
|
||||
|
||||
@private
|
||||
@(private)
|
||||
_rewinddir :: proc(dirp: Dir) {
|
||||
_unix_rewinddir(dirp)
|
||||
}
|
||||
|
||||
@private
|
||||
_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) {
|
||||
@(private, require_results)
|
||||
_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Error, end_of_stream: bool) {
|
||||
result: ^Dirent
|
||||
rc := _unix_readdir_r(dirp, &entry, &result)
|
||||
|
||||
if rc != 0 {
|
||||
err = Errno(get_last_error())
|
||||
err = get_last_error()
|
||||
return
|
||||
}
|
||||
err = ERROR_NONE
|
||||
err = nil
|
||||
|
||||
if result == nil {
|
||||
end_of_stream = true
|
||||
@@ -568,8 +694,8 @@ _readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool)
|
||||
return
|
||||
}
|
||||
|
||||
@private
|
||||
_readlink :: proc(path: string) -> (string, Errno) {
|
||||
@(private, require_results)
|
||||
_readlink :: proc(path: string) -> (string, Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.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)
|
||||
if rc == -1 {
|
||||
delete(buf)
|
||||
return "", Errno(get_last_error())
|
||||
return "", get_last_error()
|
||||
} else if rc == int(bufsz) {
|
||||
bufsz += MAX_PATH
|
||||
delete(buf)
|
||||
buf = make([]byte, bufsz)
|
||||
} else {
|
||||
return strings.string_from_ptr(&buf[0], rc), ERROR_NONE
|
||||
return strings.string_from_ptr(&buf[0], rc), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// XXX OpenBSD
|
||||
absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) {
|
||||
return "", Errno(ENOSYS)
|
||||
@(require_results)
|
||||
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
|
||||
if rel == "" {
|
||||
rel = "."
|
||||
@@ -606,25 +734,26 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
|
||||
|
||||
path_ptr := _unix_realpath(rel_cstr, nil)
|
||||
if path_ptr == nil {
|
||||
return "", Errno(get_last_error())
|
||||
return "", get_last_error()
|
||||
}
|
||||
defer _unix_free(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()
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
res := _unix_access(cstr, c.int(mask))
|
||||
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) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == 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
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
|
||||
value, _ = lookup_env(key, allocator)
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
get_current_directory :: proc() -> string {
|
||||
buf := make([dynamic]u8, MAX_PATH)
|
||||
for {
|
||||
@@ -647,7 +778,7 @@ get_current_directory :: proc() -> string {
|
||||
if cwd != nil {
|
||||
return string(cwd)
|
||||
}
|
||||
if Errno(get_last_error()) != ERANGE {
|
||||
if get_last_error() != ERANGE {
|
||||
delete(buf)
|
||||
return ""
|
||||
}
|
||||
@@ -656,14 +787,14 @@ get_current_directory :: proc() -> string {
|
||||
unreachable()
|
||||
}
|
||||
|
||||
set_current_directory :: proc(path: string) -> (err: Errno) {
|
||||
set_current_directory :: proc(path: string) -> (err: Error) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
res := _unix_chdir(cstr)
|
||||
if res == -1 {
|
||||
return Errno(get_last_error())
|
||||
return get_last_error()
|
||||
}
|
||||
return ERROR_NONE
|
||||
return nil
|
||||
}
|
||||
|
||||
exit :: proc "contextless" (code: int) -> ! {
|
||||
@@ -671,16 +802,19 @@ exit :: proc "contextless" (code: int) -> ! {
|
||||
_unix_exit(c.int(code))
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
current_thread_id :: proc "contextless" () -> int {
|
||||
return _unix_getthrid()
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
dlopen :: proc(filename: string, flags: int) -> rawptr {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
cstr := strings.clone_to_cstring(filename, context.temp_allocator)
|
||||
handle := _unix_dlopen(cstr, c.int(flags))
|
||||
return handle
|
||||
}
|
||||
@(require_results)
|
||||
dlsym :: proc(handle: rawptr, symbol: string) -> rawptr {
|
||||
assert(handle != nil)
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
@@ -692,10 +826,12 @@ dlclose :: proc(handle: rawptr) -> bool {
|
||||
assert(handle != nil)
|
||||
return _unix_dlclose(handle) == 0
|
||||
}
|
||||
@(require_results)
|
||||
dlerror :: proc() -> string {
|
||||
return string(_unix_dlerror())
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
get_page_size :: proc() -> int {
|
||||
// NOTE(tetra): The page size never changes, so why do anything complicated
|
||||
// if we don't have to.
|
||||
@@ -710,11 +846,12 @@ get_page_size :: proc() -> int {
|
||||
|
||||
_SC_NPROCESSORS_ONLN :: 503
|
||||
|
||||
@(private)
|
||||
@(private, require_results)
|
||||
_processor_core_count :: proc() -> int {
|
||||
return int(_sysconf(_SC_NPROCESSORS_ONLN))
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
_alloc_command_line_arguments :: proc() -> []string {
|
||||
res := make([]string, len(runtime.args__))
|
||||
for arg, i in runtime.args__ {
|
||||
|
||||
@@ -4,12 +4,10 @@ import "core:sys/wasm/wasi"
|
||||
import "base:runtime"
|
||||
|
||||
Handle :: distinct i32
|
||||
Errno :: distinct i32
|
||||
_Platform_Error :: wasi.errno_t
|
||||
|
||||
INVALID_HANDLE :: -1
|
||||
|
||||
ERROR_NONE :: Errno(wasi.errno_t.SUCCESS)
|
||||
|
||||
O_RDONLY :: 0x00000
|
||||
O_WRONLY :: 0x00001
|
||||
O_RDWR :: 0x00002
|
||||
@@ -29,6 +27,7 @@ stderr: Handle = 2
|
||||
|
||||
args := _alloc_command_line_arguments()
|
||||
|
||||
@(require_results)
|
||||
_alloc_command_line_arguments :: proc() -> (args: []string) {
|
||||
args = make([]string, len(runtime.args__))
|
||||
for &arg, i in args {
|
||||
@@ -93,8 +92,9 @@ init_preopens :: proc() {
|
||||
preopens = dyn_preopens[:]
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
wasi_match_preopen :: proc(path: string) -> (wasi.fd_t, string, bool) {
|
||||
|
||||
@(require_results)
|
||||
prefix_matches :: proc(prefix, path: string) -> bool {
|
||||
// Empty is valid for any relative path.
|
||||
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) {
|
||||
iovs := wasi.ciovec_t(data)
|
||||
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) {
|
||||
iovs := wasi.iovec_t(data)
|
||||
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) {
|
||||
iovs := wasi.ciovec_t(data)
|
||||
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) {
|
||||
iovs := wasi.iovec_t(data)
|
||||
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) {
|
||||
oflags: wasi.oflags_t
|
||||
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)
|
||||
return Handle(fd), Errno(err)
|
||||
return Handle(fd), Platform_Error(err)
|
||||
}
|
||||
close :: proc(fd: Handle) -> Errno {
|
||||
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) {
|
||||
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 {
|
||||
return 0
|
||||
}
|
||||
@(private)
|
||||
@(private, require_results)
|
||||
_processor_core_count :: proc() -> int {
|
||||
return 1
|
||||
}
|
||||
|
||||
file_size :: proc(fd: Handle) -> (i64, Errno) {
|
||||
stat, err := wasi.fd_filestat_get(wasi.fd_t(fd))
|
||||
if err != nil {
|
||||
return 0, Errno(err)
|
||||
}
|
||||
return i64(stat.size), 0
|
||||
@(require_results)
|
||||
file_size :: proc(fd: Handle) -> (size: i64, err: Errno) {
|
||||
stat := wasi.fd_filestat_get(wasi.fd_t(fd)) or_return
|
||||
size = i64(stat.size)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import "base:intrinsics"
|
||||
|
||||
Handle :: distinct uintptr
|
||||
File_Time :: distinct u64
|
||||
Errno :: distinct int
|
||||
|
||||
|
||||
INVALID_HANDLE :: ~Handle(0)
|
||||
@@ -27,70 +26,119 @@ O_SYNC :: 0x01000
|
||||
O_ASYNC :: 0x02000
|
||||
O_CLOEXEC :: 0x80000
|
||||
|
||||
_Platform_Error :: win32.System_Error
|
||||
|
||||
ERROR_NONE: Errno : 0
|
||||
ERROR_FILE_NOT_FOUND: Errno : 2
|
||||
ERROR_PATH_NOT_FOUND: Errno : 3
|
||||
ERROR_ACCESS_DENIED: Errno : 5
|
||||
ERROR_INVALID_HANDLE: Errno : 6
|
||||
ERROR_NOT_ENOUGH_MEMORY: Errno : 8
|
||||
ERROR_NO_MORE_FILES: Errno : 18
|
||||
ERROR_HANDLE_EOF: Errno : 38
|
||||
ERROR_NETNAME_DELETED: Errno : 64
|
||||
ERROR_FILE_EXISTS: Errno : 80
|
||||
ERROR_INVALID_PARAMETER: Errno : 87
|
||||
ERROR_BROKEN_PIPE: Errno : 109
|
||||
ERROR_BUFFER_OVERFLOW: Errno : 111
|
||||
ERROR_INSUFFICIENT_BUFFER: Errno : 122
|
||||
ERROR_MOD_NOT_FOUND: Errno : 126
|
||||
ERROR_PROC_NOT_FOUND: Errno : 127
|
||||
ERROR_DIR_NOT_EMPTY: Errno : 145
|
||||
ERROR_ALREADY_EXISTS: Errno : 183
|
||||
ERROR_ENVVAR_NOT_FOUND: Errno : 203
|
||||
ERROR_MORE_DATA: Errno : 234
|
||||
ERROR_OPERATION_ABORTED: Errno : 995
|
||||
ERROR_IO_PENDING: Errno : 997
|
||||
ERROR_NOT_FOUND: Errno : 1168
|
||||
ERROR_PRIVILEGE_NOT_HELD: Errno : 1314
|
||||
WSAEACCES: Errno : 10013
|
||||
WSAECONNRESET: Errno : 10054
|
||||
ERROR_FILE_NOT_FOUND :: _Platform_Error(2)
|
||||
ERROR_PATH_NOT_FOUND :: _Platform_Error(3)
|
||||
ERROR_ACCESS_DENIED :: _Platform_Error(5)
|
||||
ERROR_INVALID_HANDLE :: _Platform_Error(6)
|
||||
ERROR_NOT_ENOUGH_MEMORY :: _Platform_Error(8)
|
||||
ERROR_NO_MORE_FILES :: _Platform_Error(18)
|
||||
ERROR_HANDLE_EOF :: _Platform_Error(38)
|
||||
ERROR_NETNAME_DELETED :: _Platform_Error(64)
|
||||
ERROR_FILE_EXISTS :: _Platform_Error(80)
|
||||
ERROR_INVALID_PARAMETER :: _Platform_Error(87)
|
||||
ERROR_BROKEN_PIPE :: _Platform_Error(109)
|
||||
ERROR_BUFFER_OVERFLOW :: _Platform_Error(111)
|
||||
ERROR_INSUFFICIENT_BUFFER :: _Platform_Error(122)
|
||||
ERROR_MOD_NOT_FOUND :: _Platform_Error(126)
|
||||
ERROR_PROC_NOT_FOUND :: _Platform_Error(127)
|
||||
ERROR_DIR_NOT_EMPTY :: _Platform_Error(145)
|
||||
ERROR_ALREADY_EXISTS :: _Platform_Error(183)
|
||||
ERROR_ENVVAR_NOT_FOUND :: _Platform_Error(203)
|
||||
ERROR_MORE_DATA :: _Platform_Error(234)
|
||||
ERROR_OPERATION_ABORTED :: _Platform_Error(995)
|
||||
ERROR_IO_PENDING :: _Platform_Error(997)
|
||||
ERROR_NOT_FOUND :: _Platform_Error(1168)
|
||||
ERROR_PRIVILEGE_NOT_HELD :: _Platform_Error(1314)
|
||||
WSAEACCES :: _Platform_Error(10013)
|
||||
WSAECONNRESET :: _Platform_Error(10054)
|
||||
|
||||
// Windows reserves errors >= 1<<29 for application use
|
||||
ERROR_FILE_IS_PIPE: Errno : 1<<29 + 0
|
||||
ERROR_FILE_IS_NOT_DIR: Errno : 1<<29 + 1
|
||||
ERROR_NEGATIVE_OFFSET: Errno : 1<<29 + 2
|
||||
ERROR_FILE_IS_PIPE :: General_Error.File_Is_Pipe
|
||||
ERROR_FILE_IS_NOT_DIR :: General_Error.Not_Dir
|
||||
|
||||
// "Argv" arguments converted to Odin strings
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
last_write_time :: proc(fd: Handle) -> (File_Time, Errno) {
|
||||
@(require_results)
|
||||
last_write_time :: proc(fd: Handle) -> (File_Time, Error) {
|
||||
file_info: win32.BY_HANDLE_FILE_INFORMATION
|
||||
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)
|
||||
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
|
||||
|
||||
wide_path := win32.utf8_to_wstring(name)
|
||||
if !win32.GetFileAttributesExW(wide_path, win32.GetFileExInfoStandard, &data) {
|
||||
return 0, Errno(win32.GetLastError())
|
||||
return 0, get_last_error()
|
||||
}
|
||||
|
||||
l := File_Time(data.ftLastWriteTime.dwLowDateTime)
|
||||
h := File_Time(data.ftLastWriteTime.dwHighDateTime)
|
||||
return l | h << 32, ERROR_NONE
|
||||
return l | h << 32, nil
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
get_page_size :: proc() -> int {
|
||||
// NOTE(tetra): The page size never changes, so why do anything complicated
|
||||
// if we don't have to.
|
||||
@@ -105,7 +153,7 @@ get_page_size :: proc() -> int {
|
||||
return page_size
|
||||
}
|
||||
|
||||
@(private)
|
||||
@(private, require_results)
|
||||
_processor_core_count :: proc() -> int {
|
||||
length : win32.DWORD = 0
|
||||
result := win32.GetLogicalProcessorInformation(nil, &length)
|
||||
@@ -136,12 +184,14 @@ exit :: proc "contextless" (code: int) -> ! {
|
||||
|
||||
|
||||
|
||||
@(require_results)
|
||||
current_thread_id :: proc "contextless" () -> int {
|
||||
return int(win32.GetCurrentThreadId())
|
||||
}
|
||||
|
||||
|
||||
|
||||
@(require_results)
|
||||
_alloc_command_line_arguments :: proc() -> []string {
|
||||
arg_count: i32
|
||||
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
|
||||
|
||||
get_windows_version_w :: proc() -> win32.OSVERSIONINFOEXW {
|
||||
@(require_results)
|
||||
get_windows_version_w :: proc "contextless" () -> win32.OSVERSIONINFOEXW {
|
||||
osvi : win32.OSVERSIONINFOEXW
|
||||
osvi.dwOSVersionInfoSize = size_of(win32.OSVERSIONINFOEXW)
|
||||
win32.RtlGetVersion(&osvi)
|
||||
return osvi
|
||||
}
|
||||
|
||||
is_windows_xp :: proc() -> bool {
|
||||
@(require_results)
|
||||
is_windows_xp :: proc "contextless" () -> bool {
|
||||
osvi := get_windows_version_w()
|
||||
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()
|
||||
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()
|
||||
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()
|
||||
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()
|
||||
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()
|
||||
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()
|
||||
return (osvi.dwMajorVersion == 10 && osvi.dwMinorVersion == 0 && osvi.dwBuildNumber >= WINDOWS_11_BUILD_CUTOFF)
|
||||
}
|
||||
|
||||
@@ -50,14 +50,14 @@ File_Info :: struct {
|
||||
}
|
||||
*/
|
||||
|
||||
@private
|
||||
@(private, require_results)
|
||||
_make_time_from_unix_file_time :: proc(uft: Unix_File_Time) -> time.Time {
|
||||
return time.Time{
|
||||
_nsec = uft.nanoseconds + uft.seconds * 1_000_000_000,
|
||||
}
|
||||
}
|
||||
|
||||
@private
|
||||
@(private)
|
||||
_fill_file_info_from_stat :: proc(fi: ^File_Info, s: OS_Stat) {
|
||||
fi.size = s.size
|
||||
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 {
|
||||
is_separator :: proc(c: byte) -> bool {
|
||||
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
|
||||
|
||||
s: OS_Stat
|
||||
s, err = _lstat(name)
|
||||
if err != ERROR_NONE {
|
||||
return fi, err
|
||||
}
|
||||
s := _lstat(name) or_return
|
||||
_fill_file_info_from_stat(&fi, s)
|
||||
fi.fullpath, err = absolute_path_from_relative(name)
|
||||
if err != ERROR_NONE {
|
||||
return
|
||||
}
|
||||
fi.fullpath = absolute_path_from_relative(name) or_return
|
||||
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
|
||||
|
||||
s: OS_Stat
|
||||
s, err = _stat(name)
|
||||
if err != ERROR_NONE {
|
||||
return fi, err
|
||||
}
|
||||
s := _stat(name) or_return
|
||||
_fill_file_info_from_stat(&fi, s)
|
||||
fi.fullpath, err = absolute_path_from_relative(name)
|
||||
if err != ERROR_NONE {
|
||||
return
|
||||
}
|
||||
fi.fullpath = absolute_path_from_relative(name) or_return
|
||||
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
|
||||
|
||||
s: OS_Stat
|
||||
s, err = _fstat(fd)
|
||||
if err != ERROR_NONE {
|
||||
return fi, err
|
||||
}
|
||||
s := _fstat(fd) or_return
|
||||
_fill_file_info_from_stat(&fi, s)
|
||||
fi.fullpath, err = absolute_path_from_handle(fd)
|
||||
if err != ERROR_NONE {
|
||||
return
|
||||
}
|
||||
fi.fullpath = absolute_path_from_handle(fd) or_return
|
||||
fi.name = path_base(fi.fullpath)
|
||||
return fi, ERROR_NONE
|
||||
return
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import "core:time"
|
||||
import "base:runtime"
|
||||
import win32 "core:sys/windows"
|
||||
|
||||
@(private)
|
||||
@(private, require_results)
|
||||
full_path_from_name :: proc(name: string, allocator := context.allocator) -> (path: string, err: Errno) {
|
||||
context.allocator = allocator
|
||||
|
||||
@@ -19,10 +19,10 @@ full_path_from_name :: proc(name: string, allocator := context.allocator) -> (pa
|
||||
for {
|
||||
n := win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil)
|
||||
if n == 0 {
|
||||
return "", Errno(win32.GetLastError())
|
||||
return "", get_last_error()
|
||||
}
|
||||
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)
|
||||
}
|
||||
@@ -30,7 +30,7 @@ full_path_from_name :: proc(name: string, allocator := context.allocator) -> (pa
|
||||
return
|
||||
}
|
||||
|
||||
@(private)
|
||||
@(private, require_results)
|
||||
_stat :: proc(name: string, create_file_attributes: u32, allocator := context.allocator) -> (fi: File_Info, e: Errno) {
|
||||
if len(name) == 0 {
|
||||
return {}, ERROR_PATH_NOT_FOUND
|
||||
@@ -54,7 +54,7 @@ _stat :: proc(name: string, create_file_attributes: u32, allocator := context.al
|
||||
fd: win32.WIN32_FIND_DATAW
|
||||
sh := win32.FindFirstFileW(wname, &fd)
|
||||
if sh == win32.INVALID_HANDLE_VALUE {
|
||||
e = Errno(win32.GetLastError())
|
||||
e = get_last_error()
|
||||
return
|
||||
}
|
||||
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)
|
||||
if h == win32.INVALID_HANDLE_VALUE {
|
||||
e = Errno(win32.GetLastError())
|
||||
e = get_last_error()
|
||||
return
|
||||
}
|
||||
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) {
|
||||
attrs := win32.FILE_FLAG_BACKUP_SEMANTICS
|
||||
attrs |= win32.FILE_FLAG_OPEN_REPARSE_POINT
|
||||
return _stat(name, attrs, allocator)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
stat :: proc(name: string, allocator := context.allocator) -> (File_Info, Errno) {
|
||||
attrs := win32.FILE_FLAG_BACKUP_SEMANTICS
|
||||
return _stat(name, attrs, allocator)
|
||||
}
|
||||
|
||||
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 {
|
||||
return {}, ERROR_INVALID_HANDLE
|
||||
err = ERROR_INVALID_HANDLE
|
||||
}
|
||||
context.allocator = allocator
|
||||
|
||||
path, err := cleanpath_from_handle(fd)
|
||||
if err != ERROR_NONE {
|
||||
return {}, err
|
||||
path := cleanpath_from_handle(fd) or_return
|
||||
defer if err != nil {
|
||||
delete(path)
|
||||
}
|
||||
|
||||
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:
|
||||
fi.name = basename(path)
|
||||
fi.mode |= file_type_mode(h)
|
||||
errno = ERROR_NONE
|
||||
err = nil
|
||||
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
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@(private)
|
||||
@(private, require_results)
|
||||
cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 {
|
||||
buf := buf
|
||||
N := 0
|
||||
@@ -133,16 +136,13 @@ cleanpath_strip_prefix :: proc(buf: []u16) -> []u16 {
|
||||
return buf
|
||||
}
|
||||
|
||||
@(private)
|
||||
cleanpath_from_handle :: proc(fd: Handle) -> (string, Errno) {
|
||||
@(private, require_results)
|
||||
cleanpath_from_handle :: proc(fd: Handle) -> (s: string, err: Errno) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
|
||||
buf, err := cleanpath_from_handle_u16(fd, context.temp_allocator)
|
||||
if err != 0 {
|
||||
return "", err
|
||||
}
|
||||
return win32.utf16_to_utf8(buf, context.allocator) or_else "", err
|
||||
buf := cleanpath_from_handle_u16(fd, context.temp_allocator) or_return
|
||||
return win32.utf16_to_utf8(buf, context.allocator)
|
||||
}
|
||||
@(private)
|
||||
@(private, require_results)
|
||||
cleanpath_from_handle_u16 :: proc(fd: Handle, allocator: runtime.Allocator) -> ([]u16, Errno) {
|
||||
if fd == 0 {
|
||||
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)
|
||||
if n == 0 {
|
||||
return nil, Errno(win32.GetLastError())
|
||||
return nil, get_last_error()
|
||||
}
|
||||
buf := make([]u16, max(n, win32.DWORD(260))+1, allocator)
|
||||
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 {
|
||||
buf := buf
|
||||
buf = cleanpath_strip_prefix(buf)
|
||||
return win32.utf16_to_utf8(buf, context.allocator) or_else ""
|
||||
}
|
||||
|
||||
@(private)
|
||||
@(private, require_results)
|
||||
basename :: proc(name: string) -> (base: string) {
|
||||
name := name
|
||||
if len(name) > 3 && name[:3] == `\\?` {
|
||||
@@ -190,7 +190,7 @@ basename :: proc(name: string) -> (base: string) {
|
||||
return name
|
||||
}
|
||||
|
||||
@(private)
|
||||
@(private, require_results)
|
||||
file_type_mode :: proc(h: win32.HANDLE) -> File_Mode {
|
||||
switch win32.GetFileType(h) {
|
||||
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) {
|
||||
if FileAttributes & win32.FILE_ATTRIBUTE_READONLY != 0 {
|
||||
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))
|
||||
}
|
||||
|
||||
@(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) {
|
||||
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
|
||||
}
|
||||
|
||||
@(private)
|
||||
@(private, require_results)
|
||||
file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string) -> (fi: File_Info, e: Errno) {
|
||||
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
|
||||
|
||||
@@ -269,20 +269,20 @@ file_info_from_win32_find_data :: proc(d: ^win32.WIN32_FIND_DATAW, name: string)
|
||||
return
|
||||
}
|
||||
|
||||
@(private)
|
||||
@(private, require_results)
|
||||
file_info_from_get_file_information_by_handle :: proc(path: string, h: win32.HANDLE) -> (File_Info, Errno) {
|
||||
d: win32.BY_HANDLE_FILE_INFORMATION
|
||||
if !win32.GetFileInformationByHandle(h, &d) {
|
||||
err := Errno(win32.GetLastError())
|
||||
err := get_last_error()
|
||||
return {}, err
|
||||
|
||||
}
|
||||
|
||||
ti: win32.FILE_ATTRIBUTE_TAG_INFO
|
||||
if !win32.GetFileInformationByHandleEx(h, .FileAttributeTagInfo, &ti, size_of(ti)) {
|
||||
err := win32.GetLastError()
|
||||
if err != u32(ERROR_INVALID_PARAMETER) {
|
||||
return {}, Errno(err)
|
||||
err := get_last_error()
|
||||
if err != ERROR_INVALID_PARAMETER {
|
||||
return {}, err
|
||||
}
|
||||
// Indicate this is a symlink on FAT file systems
|
||||
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)
|
||||
|
||||
return fi, ERROR_NONE
|
||||
return fi, nil
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
fd := Handle(uintptr(stream_data))
|
||||
n_int: int
|
||||
os_err: Errno
|
||||
os_err: Error
|
||||
switch mode {
|
||||
case .Close:
|
||||
close(fd)
|
||||
os_err = close(fd)
|
||||
case .Flush:
|
||||
when ODIN_OS == .Windows {
|
||||
flush(fd)
|
||||
} else {
|
||||
// TOOD(bill): other operating systems
|
||||
}
|
||||
os_err = flush(fd)
|
||||
case .Read:
|
||||
n_int, os_err = read(fd, p)
|
||||
n = i64(n_int)
|
||||
if n == 0 && os_err == 0 {
|
||||
if n == 0 && os_err == nil {
|
||||
err = .EOF
|
||||
}
|
||||
|
||||
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 = i64(n_int)
|
||||
if n == 0 && os_err == 0 {
|
||||
err = .EOF
|
||||
}
|
||||
n_int, os_err = read_at(fd, p, offset)
|
||||
n = i64(n_int)
|
||||
if n == 0 && os_err == nil {
|
||||
err = .EOF
|
||||
}
|
||||
case .Write:
|
||||
n_int, os_err = write(fd, p)
|
||||
n = i64(n_int)
|
||||
if n == 0 && os_err == 0 {
|
||||
if n == 0 && os_err == nil {
|
||||
err = .EOF
|
||||
}
|
||||
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 = i64(n_int)
|
||||
if n == 0 && os_err == 0 {
|
||||
err = .EOF
|
||||
}
|
||||
n_int, os_err = write_at(fd, p, offset)
|
||||
n = i64(n_int)
|
||||
if n == 0 && os_err == nil {
|
||||
err = .EOF
|
||||
}
|
||||
case .Seek:
|
||||
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:
|
||||
err = .Empty
|
||||
case .Query:
|
||||
when ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Haiku {
|
||||
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})
|
||||
}
|
||||
return io.query_utility({.Close, .Flush, .Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Query})
|
||||
}
|
||||
|
||||
if err == nil && os_err != 0 {
|
||||
when ODIN_OS == .Windows {
|
||||
if os_err == ERROR_HANDLE_EOF {
|
||||
return n, .EOF
|
||||
}
|
||||
}
|
||||
err = .Unknown
|
||||
if err == nil && os_err != nil {
|
||||
err = error_to_io_error(os_err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -271,7 +271,7 @@ _glob :: proc(dir, pattern: string, matches: ^[dynamic]string, allocator := cont
|
||||
|
||||
|
||||
d, derr := os.open(dir, os.O_RDONLY)
|
||||
if derr != 0 {
|
||||
if derr != nil {
|
||||
return
|
||||
}
|
||||
defer os.close(d)
|
||||
@@ -280,7 +280,7 @@ _glob :: proc(dir, pattern: string, matches: ^[dynamic]string, allocator := cont
|
||||
file_info, ferr := os.fstat(d)
|
||||
defer os.file_info_delete(file_info)
|
||||
|
||||
if ferr != 0 {
|
||||
if ferr != nil {
|
||||
return
|
||||
}
|
||||
if !file_info.is_dir {
|
||||
|
||||
@@ -52,7 +52,7 @@ is_abs :: proc(path: string) -> bool {
|
||||
|
||||
|
||||
@(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
|
||||
|
||||
name := name
|
||||
@@ -63,17 +63,17 @@ temp_full_path :: proc(name: string) -> (path: string, err: os.Errno) {
|
||||
p := win32.utf8_to_utf16(name, ta)
|
||||
n := win32.GetFullPathNameW(raw_data(p), 0, nil, nil)
|
||||
if n == 0 {
|
||||
return "", os.Errno(win32.GetLastError())
|
||||
return "", os.get_last_error()
|
||||
}
|
||||
|
||||
buf := make([]u16, n, ta)
|
||||
n = win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil)
|
||||
if n == 0 {
|
||||
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) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = allocator == context.temp_allocator)
|
||||
full_path, err := temp_full_path(path)
|
||||
if err != 0 {
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
p := clean(full_path, allocator)
|
||||
|
||||
@@ -14,7 +14,7 @@ import "core:slice"
|
||||
// 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 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'
|
||||
// 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: walk does not follow symbolic links
|
||||
// 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)
|
||||
defer os.file_info_delete(info, context.temp_allocator)
|
||||
|
||||
skip_dir: bool
|
||||
if err != 0 {
|
||||
if err != nil {
|
||||
err, skip_dir = walk_proc(info, err, user_data)
|
||||
} else {
|
||||
err, skip_dir = _walk(info, walk_proc, user_data)
|
||||
}
|
||||
return 0 if skip_dir else err
|
||||
return nil if skip_dir else err
|
||||
}
|
||||
|
||||
|
||||
@(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.fullpath == "" && info.name == "" {
|
||||
// ignore empty things
|
||||
return
|
||||
}
|
||||
return walk_proc(info, 0, user_data)
|
||||
return walk_proc(info, nil, user_data)
|
||||
}
|
||||
|
||||
fis: []os.File_Info
|
||||
err1: os.Errno
|
||||
err1: os.Error
|
||||
fis, err = read_dir(info.fullpath, context.temp_allocator)
|
||||
defer os.file_info_slice_delete(fis, context.temp_allocator)
|
||||
|
||||
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
|
||||
return
|
||||
}
|
||||
|
||||
for fi in fis {
|
||||
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 {
|
||||
return
|
||||
}
|
||||
@@ -70,19 +70,12 @@ _walk :: proc(info: os.File_Info, walk_proc: Walk_Proc, user_data: rawptr) -> (e
|
||||
}
|
||||
|
||||
@(private)
|
||||
read_dir :: proc(dir_name: string, allocator := context.temp_allocator) -> ([]os.File_Info, os.Errno) {
|
||||
f, err := os.open(dir_name, os.O_RDONLY)
|
||||
if err != 0 {
|
||||
return nil, err
|
||||
}
|
||||
fis: []os.File_Info
|
||||
fis, err = os.read_dir(f, -1, allocator)
|
||||
os.close(f)
|
||||
if err != 0 {
|
||||
return nil, err
|
||||
}
|
||||
read_dir :: proc(dir_name: string, allocator := context.temp_allocator) -> (fis: []os.File_Info, err: os.Error) {
|
||||
f := os.open(dir_name, os.O_RDONLY) or_return
|
||||
defer os.close(f)
|
||||
fis = os.read_dir(f, -1, allocator) or_return
|
||||
slice.sort_by(fis, proc(a, b: os.File_Info) -> bool {
|
||||
return a.name < b.name
|
||||
})
|
||||
return fis, 0
|
||||
return
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -227,7 +227,7 @@ _buffer_end :: proc "contextless" (ctx: ^Context, buffer: ^Buffer) #no_bounds_ch
|
||||
}
|
||||
|
||||
@(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)
|
||||
}
|
||||
|
||||
|
||||
@@ -10,21 +10,12 @@ import "core:sys/linux"
|
||||
MAX_RW :: 0x7fffffff
|
||||
|
||||
@(no_instrumentation)
|
||||
_write :: proc "contextless" (fd: os.Handle, data: []byte) -> (n: int, err: os.Errno) #no_bounds_check /* bounds check would segfault instrumentation */ {
|
||||
if len(data) == 0 {
|
||||
return 0, os.ERROR_NONE
|
||||
}
|
||||
|
||||
_write :: proc "contextless" (fd: os.Handle, data: []byte) -> (n: int, err: os.Error) #no_bounds_check /* bounds check would segfault instrumentation */ {
|
||||
for n < len(data) {
|
||||
chunk := data[:min(len(data), MAX_RW)]
|
||||
written, errno := linux.write(linux.Fd(fd), chunk)
|
||||
if errno != .NONE {
|
||||
return n, os.Errno(errno)
|
||||
}
|
||||
n += written
|
||||
n += linux.write(linux.Fd(fd), chunk) or_return
|
||||
}
|
||||
|
||||
return n, os.ERROR_NONE
|
||||
return
|
||||
}
|
||||
|
||||
CLOCK_MONOTONIC_RAW :: 4 // NOTE(tetra): "RAW" means: Not adjusted by NTP.
|
||||
|
||||
@@ -22,29 +22,24 @@ foreign libc {
|
||||
@(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
|
||||
|
||||
@(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
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
for n < len(data) {
|
||||
chunk := data[:min(len(data), MAX_RW)]
|
||||
written := _unix_write(fd, raw_data(chunk), len(chunk))
|
||||
if written < 0 {
|
||||
return n, os.Errno(get_last_error())
|
||||
return n, os.get_last_error()
|
||||
}
|
||||
n += written
|
||||
}
|
||||
|
||||
return n, os.ERROR_NONE
|
||||
return n, nil
|
||||
}
|
||||
|
||||
CLOCK_MONOTONIC_RAW :: 4 // NOTE(tetra): "RAW" means: Not adjusted by NTP.
|
||||
|
||||
@@ -10,9 +10,9 @@ import win32 "core:sys/windows"
|
||||
MAX_RW :: 1<<30
|
||||
|
||||
@(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 {
|
||||
return 0, os.ERROR_NONE
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
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)
|
||||
if single_write_length <= 0 || !e {
|
||||
err := os.Errno(win32.GetLastError())
|
||||
return int(total_write), err
|
||||
return int(total_write), os.get_last_error()
|
||||
}
|
||||
total_write += i64(single_write_length)
|
||||
}
|
||||
return int(total_write), os.ERROR_NONE
|
||||
return int(total_write), nil
|
||||
}
|
||||
|
||||
@(no_instrumentation)
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
package sync
|
||||
|
||||
import "core:c"
|
||||
import "base:runtime"
|
||||
import "core:sys/haiku"
|
||||
import "core:sys/unix"
|
||||
import "core:time"
|
||||
@@ -86,10 +85,10 @@ _futex_wait :: proc "contextless" (f: ^Futex, expect: u32) -> (ok: bool) {
|
||||
waiter.prev.next = waiter.next
|
||||
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!
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
_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.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!
|
||||
return
|
||||
// FIXME: Add error handling!
|
||||
return
|
||||
}
|
||||
|
||||
_futex_signal :: proc "contextless" (f: ^Futex) {
|
||||
|
||||
@@ -399,7 +399,7 @@ cpu_topology_node_info :: struct {
|
||||
},
|
||||
_package: struct {
|
||||
vendor: cpu_vendor,
|
||||
cache_line_size: u32
|
||||
cache_line_size: u32,
|
||||
},
|
||||
_core: struct {
|
||||
model: u32,
|
||||
|
||||
@@ -16,6 +16,7 @@ CLOCK_REALTIME :: clockid_t(2)
|
||||
CLOCK_THREAD_CPUTIME_ID :: clockid_t(3)
|
||||
|
||||
errno_t :: enum u16 {
|
||||
NONE = 0,
|
||||
// No error occurred. System call completed successfully.
|
||||
SUCCESS = 0,
|
||||
// Argument list too long.
|
||||
|
||||
@@ -868,8 +868,8 @@ To partly mitigate this, redirect STDERR to a file or use the -define:ODIN_TEST_
|
||||
when ODIN_OS != .Windows {
|
||||
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)
|
||||
fmt.assertf(errno == os.ERROR_NONE, "unable to open file %q for writing of JSON report, error: %v", JSON_REPORT, errno)
|
||||
json_fd, err := os.open(JSON_REPORT, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, mode)
|
||||
fmt.assertf(err == nil, "unable to open file %q for writing of JSON report, error: %v", JSON_REPORT, err)
|
||||
defer os.close(json_fd)
|
||||
|
||||
for test, i in report.all_tests {
|
||||
|
||||
@@ -359,7 +359,7 @@ control_flow :: proc() {
|
||||
|
||||
if false {
|
||||
f, err := os.open("my_file.txt")
|
||||
if err != os.ERROR_NONE {
|
||||
if err != nil {
|
||||
// handle error
|
||||
}
|
||||
defer os.close(f)
|
||||
|
||||
@@ -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 is_exact_value_zero(ExactValue const &v);
|
||||
|
||||
enum LoadDirectiveResult {
|
||||
LoadDirective_Success = 0,
|
||||
LoadDirective_Error = 1,
|
||||
@@ -4457,6 +4459,27 @@ gb_internal void convert_to_typed(CheckerContext *c, Operand *operand, Type *tar
|
||||
|
||||
|
||||
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)) {
|
||||
TEMPORARY_ALLOCATOR_GUARD();
|
||||
|
||||
|
||||
@@ -1117,7 +1117,7 @@ test_os_handle :: proc(t: ^testing.T) {
|
||||
// Delete the file now that we're done.
|
||||
//
|
||||
// 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"
|
||||
|
||||
@@ -7,12 +7,12 @@ import "core:testing"
|
||||
|
||||
@(test)
|
||||
read_dir :: proc(t: ^testing.T) {
|
||||
fd, errno := os.open(#directory + "/dir")
|
||||
testing.expect_value(t, errno, os.ERROR_NONE)
|
||||
fd, err := os.open(#directory + "/dir")
|
||||
testing.expect_value(t, err, nil)
|
||||
defer os.close(fd)
|
||||
|
||||
dir, errno2 := os.read_dir(fd, -1)
|
||||
testing.expect_value(t, errno2, os.ERROR_NONE)
|
||||
dir, err2 := os.read_dir(fd, -1)
|
||||
testing.expect_value(t, err2, nil)
|
||||
defer os.file_info_slice_delete(dir)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
g_bad_doc = true
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user