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

`os.Error` to replace `os.Errno`
This commit is contained in:
gingerBill
2024-08-04 15:53:18 +01:00
committed by GitHub
49 changed files with 2904 additions and 1808 deletions

View File

@@ -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

View File

@@ -38,9 +38,9 @@ iterate_csv_from_stream :: proc(filename: string) {
r.reuse_record_buffer = true // Without it you have to each of the fields within it
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)

View File

@@ -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,
}

View File

@@ -254,8 +254,8 @@ parse_and_set_pointer_by_named_type :: proc(ptr: rawptr, str: string, data_type:
}
handle, errno := os.open(str, mode, perms)
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.

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -24,7 +24,7 @@ map_file :: proc{
map_file_from_path :: proc(filename: string, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) {
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 {

View File

@@ -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

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -7,27 +7,22 @@ import "base:runtime"
// If the variable is found in the environment the value (which can be empty) is returned and the boolean is true
// 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
View File

@@ -0,0 +1,322 @@
package os
import "base:intrinsics"
import "base:runtime"
import "core:io"
Platform_Error :: _Platform_Error
#assert(size_of(Platform_Error) <= 4)
#assert(intrinsics.type_has_nil(Platform_Error))
General_Error :: enum u32 {
None,
Permission_Denied,
Exist,
Not_Exist,
Closed,
Timeout,
Broken_Pipe,
// Indicates that an attempt to retrieve a file's size was made, but the
// file doesn't have a size.
No_Size,
Invalid_File,
Invalid_Dir,
Invalid_Path,
Invalid_Callback,
Pattern_Has_Separator,
Unsupported,
File_Is_Pipe,
Not_Dir,
}
Errno :: Error // alias for legacy use
Error :: union #shared_nil {
General_Error,
io.Error,
runtime.Allocator_Error,
Platform_Error,
}
#assert(size_of(Error) == 8)
ERROR_NONE :: Error{}
ERROR_EOF :: io.Error.EOF
@(require_results)
is_platform_error :: proc "contextless" (ferr: Error) -> (err: i32, ok: bool) {
v := ferr.(Platform_Error) or_else {}
return i32(v), i32(v) != 0
}
@(require_results)
error_string :: proc "contextless" (ferr: Error) -> string {
if ferr == nil {
return ""
}
switch e in ferr {
case General_Error:
switch e {
case .None: return ""
case .Permission_Denied: return "permission denied"
case .Exist: return "file already exists"
case .Not_Exist: return "file does not exist"
case .Closed: return "file already closed"
case .Timeout: return "i/o timeout"
case .Broken_Pipe: return "Broken pipe"
case .No_Size: return "file has no definite size"
case .Invalid_File: return "invalid file"
case .Invalid_Dir: return "invalid directory"
case .Invalid_Path: return "invalid path"
case .Invalid_Callback: return "invalid callback"
case .Unsupported: return "unsupported"
case .Pattern_Has_Separator: return "pattern has separator"
case .File_Is_Pipe: return "file is pipe"
case .Not_Dir: return "file is not directory"
}
case io.Error:
switch e {
case .None: return ""
case .EOF: return "eof"
case .Unexpected_EOF: return "unexpected eof"
case .Short_Write: return "short write"
case .Invalid_Write: return "invalid write result"
case .Short_Buffer: return "short buffer"
case .No_Progress: return "multiple read calls return no data or error"
case .Invalid_Whence: return "invalid whence"
case .Invalid_Offset: return "invalid offset"
case .Invalid_Unread: return "invalid unread"
case .Negative_Read: return "negative read"
case .Negative_Write: return "negative write"
case .Negative_Count: return "negative count"
case .Buffer_Full: return "buffer full"
case .Unknown, .Empty: //
}
case runtime.Allocator_Error:
switch e {
case .None: return ""
case .Out_Of_Memory: return "out of memory"
case .Invalid_Pointer: return "invalid allocator pointer"
case .Invalid_Argument: return "invalid allocator argument"
case .Mode_Not_Implemented: return "allocator mode not implemented"
}
case Platform_Error:
return _error_string(e)
}
return "unknown error"
}
print_error :: proc(f: Handle, ferr: Error, msg: string) -> (n: int, err: Error) {
err_str := error_string(ferr)
// msg + ": " + err_str + '\n'
length := len(msg) + 2 + len(err_str) + 1
buf_ := intrinsics.alloca(length, 1)
buf := buf_[:length]
copy(buf, msg)
buf[len(msg)] = ':'
buf[len(msg) + 1] = ' '
copy(buf[len(msg) + 2:], err_str)
buf[length - 1] = '\n'
return write(f, buf)
}
@(require_results, private)
_error_string :: proc "contextless" (e: Platform_Error) -> string where intrinsics.type_is_enum(Platform_Error) {
if e == nil {
return ""
}
when ODIN_OS == .Darwin {
if s := string(_darwin_string_error(i32(e))); s != "" {
return s
}
}
when ODIN_OS != .Linux {
@(require_results)
binary_search :: proc "contextless" (array: $A/[]$T, key: T) -> (index: int, found: bool) #no_bounds_check {
n := len(array)
left, right := 0, n
for left < right {
mid := int(uint(left+right) >> 1)
if array[mid] < key {
left = mid+1
} else {
// equal or greater
right = mid
}
}
return left, left < n && array[left] == key
}
err := runtime.Type_Info_Enum_Value(e)
ti := &runtime.type_info_base(type_info_of(Platform_Error)).variant.(runtime.Type_Info_Enum)
if idx, ok := binary_search(ti.values, err); ok {
return ti.names[idx]
}
} else {
@(rodata, static)
pe_strings := [Platform_Error]string{
.NONE = "",
.EPERM = "Operation not permitted",
.ENOENT = "No such file or directory",
.ESRCH = "No such process",
.EINTR = "Interrupted system call",
.EIO = "Input/output error",
.ENXIO = "No such device or address",
.E2BIG = "Argument list too long",
.ENOEXEC = "Exec format error",
.EBADF = "Bad file descriptor",
.ECHILD = "No child processes",
.EAGAIN = "Resource temporarily unavailable",
.ENOMEM = "Cannot allocate memory",
.EACCES = "Permission denied",
.EFAULT = "Bad address",
.ENOTBLK = "Block device required",
.EBUSY = "Device or resource busy",
.EEXIST = "File exists",
.EXDEV = "Invalid cross-device link",
.ENODEV = "No such device",
.ENOTDIR = "Not a directory",
.EISDIR = "Is a directory",
.EINVAL = "Invalid argument",
.ENFILE = "Too many open files in system",
.EMFILE = "Too many open files",
.ENOTTY = "Inappropriate ioctl for device",
.ETXTBSY = "Text file busy",
.EFBIG = "File too large",
.ENOSPC = "No space left on device",
.ESPIPE = "Illegal seek",
.EROFS = "Read-only file system",
.EMLINK = "Too many links",
.EPIPE = "Broken pipe",
.EDOM = "Numerical argument out of domain",
.ERANGE = "Numerical result out of range",
.EDEADLK = "Resource deadlock avoided",
.ENAMETOOLONG = "File name too long",
.ENOLCK = "No locks available",
.ENOSYS = "Function not implemented",
.ENOTEMPTY = "Directory not empty",
.ELOOP = "Too many levels of symbolic links",
.EUNKNOWN_41 = "Unknown Error (41)",
.ENOMSG = "No message of desired type",
.EIDRM = "Identifier removed",
.ECHRNG = "Channel number out of range",
.EL2NSYNC = "Level 2 not synchronized",
.EL3HLT = "Level 3 halted",
.EL3RST = "Level 3 reset",
.ELNRNG = "Link number out of range",
.EUNATCH = "Protocol driver not attached",
.ENOCSI = "No CSI structure available",
.EL2HLT = "Level 2 halted",
.EBADE = "Invalid exchange",
.EBADR = "Invalid request descriptor",
.EXFULL = "Exchange full",
.ENOANO = "No anode",
.EBADRQC = "Invalid request code",
.EBADSLT = "Invalid slot",
.EUNKNOWN_58 = "Unknown Error (58)",
.EBFONT = "Bad font file format",
.ENOSTR = "Device not a stream",
.ENODATA = "No data available",
.ETIME = "Timer expired",
.ENOSR = "Out of streams resources",
.ENONET = "Machine is not on the network",
.ENOPKG = "Package not installed",
.EREMOTE = "Object is remote",
.ENOLINK = "Link has been severed",
.EADV = "Advertise error",
.ESRMNT = "Srmount error",
.ECOMM = "Communication error on send",
.EPROTO = "Protocol error",
.EMULTIHOP = "Multihop attempted",
.EDOTDOT = "RFS specific error",
.EBADMSG = "Bad message",
.EOVERFLOW = "Value too large for defined data type",
.ENOTUNIQ = "Name not unique on network",
.EBADFD = "File descriptor in bad state",
.EREMCHG = "Remote address changed",
.ELIBACC = "Can not access a needed shared library",
.ELIBBAD = "Accessing a corrupted shared library",
.ELIBSCN = ".lib section in a.out corrupted",
.ELIBMAX = "Attempting to link in too many shared libraries",
.ELIBEXEC = "Cannot exec a shared library directly",
.EILSEQ = "Invalid or incomplete multibyte or wide character",
.ERESTART = "Interrupted system call should be restarted",
.ESTRPIPE = "Streams pipe error",
.EUSERS = "Too many users",
.ENOTSOCK = "Socket operation on non-socket",
.EDESTADDRREQ = "Destination address required",
.EMSGSIZE = "Message too long",
.EPROTOTYPE = "Protocol wrong type for socket",
.ENOPROTOOPT = "Protocol not available",
.EPROTONOSUPPORT = "Protocol not supported",
.ESOCKTNOSUPPORT = "Socket type not supported",
.EOPNOTSUPP = "Operation not supported",
.EPFNOSUPPORT = "Protocol family not supported",
.EAFNOSUPPORT = "Address family not supported by protocol",
.EADDRINUSE = "Address already in use",
.EADDRNOTAVAIL = "Cannot assign requested address",
.ENETDOWN = "Network is down",
.ENETUNREACH = "Network is unreachable",
.ENETRESET = "Network dropped connection on reset",
.ECONNABORTED = "Software caused connection abort",
.ECONNRESET = "Connection reset by peer",
.ENOBUFS = "No buffer space available",
.EISCONN = "Transport endpoint is already connected",
.ENOTCONN = "Transport endpoint is not connected",
.ESHUTDOWN = "Cannot send after transport endpoint shutdown",
.ETOOMANYREFS = "Too many references: cannot splice",
.ETIMEDOUT = "Connection timed out",
.ECONNREFUSED = "Connection refused",
.EHOSTDOWN = "Host is down",
.EHOSTUNREACH = "No route to host",
.EALREADY = "Operation already in progress",
.EINPROGRESS = "Operation now in progress",
.ESTALE = "Stale file handle",
.EUCLEAN = "Structure needs cleaning",
.ENOTNAM = "Not a XENIX named type file",
.ENAVAIL = "No XENIX semaphores available",
.EISNAM = "Is a named type file",
.EREMOTEIO = "Remote I/O error",
.EDQUOT = "Disk quota exceeded",
.ENOMEDIUM = "No medium found",
.EMEDIUMTYPE = "Wrong medium type",
.ECANCELED = "Operation canceled",
.ENOKEY = "Required key not available",
.EKEYEXPIRED = "Key has expired",
.EKEYREVOKED = "Key has been revoked",
.EKEYREJECTED = "Key was rejected by service",
.EOWNERDEAD = "Owner died",
.ENOTRECOVERABLE = "State not recoverable",
.ERFKILL = "Operation not possible due to RF-kill",
.EHWPOISON = "Memory page has hardware error",
}
if Platform_Error.NONE <= e && e <= max(Platform_Error) {
return pe_strings[e]
}
}
return "<unknown platform error>"
}
@(private, require_results)
error_to_io_error :: proc(ferr: Error) -> io.Error {
if ferr == nil {
return .None
}
return ferr.(io.Error) or_else .Unknown
}

View File

@@ -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
}

View File

@@ -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()
}

View File

@@ -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 ""

View File

@@ -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"
}

View File

@@ -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
}

View File

@@ -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:]

View File

@@ -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

View File

@@ -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
}

View File

@@ -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__ {

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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__ {

View File

@@ -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__ {

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -50,14 +50,14 @@ File_Info :: struct {
}
*/
@private
@(private, require_results)
_make_time_from_unix_file_time :: proc(uft: Unix_File_Time) -> time.Time {
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
}

View File

@@ -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
}

View File

@@ -14,44 +14,36 @@ stream_from_handle :: proc(fd: Handle) -> io.Stream {
_file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
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
}

View File

@@ -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 {

View File

@@ -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)

View File

@@ -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
}

View File

@@ -68,7 +68,7 @@ BUFFER_DEFAULT_SIZE :: 0x10_0000
context_create_with_scale :: proc(filename: string, precise_time: bool, timestamp_scale: f64) -> (ctx: Context, ok: bool) #optional_ok {
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)
}

View File

@@ -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.

View File

@@ -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.

View File

@@ -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)

View File

@@ -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) {

View File

@@ -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,

View File

@@ -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.

View File

@@ -868,8 +868,8 @@ To partly mitigate this, redirect STDERR to a file or use the -define:ODIN_TEST_
when ODIN_OS != .Windows {
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 {

View File

@@ -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)

View File

@@ -127,6 +127,8 @@ gb_internal bool complete_soa_type(Checker *checker, Type *t, bool wait_to_finis
gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type *y);
gb_internal bool 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();

View File

@@ -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"

View File

@@ -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 })

View File

@@ -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