mirror of
https://github.com/odin-lang/Odin.git
synced 2026-02-19 01:18:22 +00:00
Merge branch 'master' of https://github.com/odin-lang/Odin
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
//+private
|
||||
//+build linux, darwin, freebsd, openbsd
|
||||
//+build linux, darwin, freebsd, openbsd, haiku
|
||||
//+no-instrumentation
|
||||
package runtime
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//+build linux, darwin, freebsd, openbsd
|
||||
//+build linux, darwin, freebsd, openbsd, haiku
|
||||
//+private
|
||||
package runtime
|
||||
|
||||
@@ -35,4 +35,4 @@ _heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||
|
||||
_heap_free :: proc(ptr: rawptr) {
|
||||
_unix_free(ptr)
|
||||
}
|
||||
}
|
||||
|
||||
21
base/runtime/os_specific_haiku.odin
Normal file
21
base/runtime/os_specific_haiku.odin
Normal file
@@ -0,0 +1,21 @@
|
||||
//+build haiku
|
||||
//+private
|
||||
package runtime
|
||||
|
||||
foreign import libc "system:c"
|
||||
|
||||
foreign libc {
|
||||
@(link_name="write")
|
||||
_unix_write :: proc(fd: i32, buf: rawptr, size: int) -> int ---
|
||||
|
||||
_errnop :: proc() -> ^i32 ---
|
||||
}
|
||||
|
||||
_stderr_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
|
||||
ret := _unix_write(2, raw_data(data), len(data))
|
||||
if ret < len(data) {
|
||||
err := _errnop()
|
||||
return int(ret), _OS_Errno(err^ if err != nil else 0)
|
||||
}
|
||||
return int(ret), 0
|
||||
}
|
||||
@@ -82,6 +82,11 @@ OpenBSD)
|
||||
LDFLAGS="$LDFLAGS -liconv"
|
||||
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
|
||||
;;
|
||||
Haiku)
|
||||
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags) -I/system/develop/headers/private/shared -I/system/develop/headers/private/kernel"
|
||||
LDFLAGS="$LDFLAGS -liconv"
|
||||
LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
|
||||
;;
|
||||
*)
|
||||
error "Platform \"$OS_NAME\" unsupported"
|
||||
;;
|
||||
|
||||
@@ -80,6 +80,24 @@ when ODIN_OS == .Darwin {
|
||||
ERANGE :: 34
|
||||
}
|
||||
|
||||
when ODIN_OS == .Haiku {
|
||||
@(private="file")
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
@(link_name="_errnop")
|
||||
_get_errno :: proc() -> ^int ---
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
B_GENERAL_ERROR_BASE :: min(i32)
|
||||
@(private="file")
|
||||
B_POSIX_ERROR_BASE :: B_GENERAL_ERROR_BASE + 0x7000
|
||||
|
||||
EDOM :: B_POSIX_ERROR_BASE + 16
|
||||
EILSEQ :: B_POSIX_ERROR_BASE + 38
|
||||
ERANGE :: B_POSIX_ERROR_BASE + 17
|
||||
}
|
||||
|
||||
// Odin has no way to make an identifier "errno" behave as a function call to
|
||||
// read the value, or to produce an lvalue such that you can assign a different
|
||||
// error value to errno. To work around this, just expose it as a function like
|
||||
|
||||
@@ -163,6 +163,36 @@ when ODIN_OS == .Darwin {
|
||||
}
|
||||
}
|
||||
|
||||
when ODIN_OS == .Haiku {
|
||||
fpos_t :: distinct i64
|
||||
|
||||
_IOFBF :: 0
|
||||
_IOLBF :: 1
|
||||
_IONBF :: 2
|
||||
|
||||
BUFSIZ :: 8192
|
||||
|
||||
EOF :: int(-1)
|
||||
|
||||
FOPEN_MAX :: 128
|
||||
|
||||
FILENAME_MAX :: 256
|
||||
|
||||
L_tmpnam :: 512
|
||||
|
||||
SEEK_SET :: 0
|
||||
SEEK_CUR :: 1
|
||||
SEEK_END :: 2
|
||||
|
||||
TMP_MAX :: 32768
|
||||
|
||||
foreign libc {
|
||||
stderr: ^FILE
|
||||
stdin: ^FILE
|
||||
stdout: ^FILE
|
||||
}
|
||||
}
|
||||
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
// 7.21.4 Operations on files
|
||||
|
||||
@@ -45,7 +45,7 @@ when ODIN_OS == .Windows {
|
||||
}
|
||||
}
|
||||
|
||||
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD {
|
||||
when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS == .OpenBSD || ODIN_OS == .Haiku {
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
// 7.27.2 Time manipulation functions
|
||||
|
||||
@@ -29,7 +29,11 @@ when ODIN_OS == .Windows {
|
||||
} else when ODIN_OS == .FreeBSD {
|
||||
wctrans_t :: distinct int
|
||||
wctype_t :: distinct ulong
|
||||
|
||||
|
||||
} else when ODIN_OS == .Haiku {
|
||||
wctrans_t :: distinct i32
|
||||
wctype_t :: distinct i32
|
||||
|
||||
}
|
||||
|
||||
@(default_calling_convention="c")
|
||||
|
||||
435
core/os/os_haiku.odin
Normal file
435
core/os/os_haiku.odin
Normal file
@@ -0,0 +1,435 @@
|
||||
package os
|
||||
|
||||
foreign import libc "system:c"
|
||||
|
||||
import "base:runtime"
|
||||
import "core:c"
|
||||
import "core:strings"
|
||||
import "core:sys/haiku"
|
||||
|
||||
Handle :: i32
|
||||
Pid :: i32
|
||||
File_Time :: i64
|
||||
Errno :: i32
|
||||
|
||||
MAX_PATH :: haiku.PATH_MAX
|
||||
|
||||
ENOSYS :: int(haiku.Errno.POSIX_ERROR_BASE) + 9
|
||||
|
||||
INVALID_HANDLE :: ~Handle(0)
|
||||
|
||||
ERROR_NONE: Errno: 0
|
||||
|
||||
stdin: Handle = 0
|
||||
stdout: Handle = 1
|
||||
stderr: Handle = 2
|
||||
|
||||
pid_t :: haiku.pid_t
|
||||
off_t :: haiku.off_t
|
||||
dev_t :: haiku.dev_t
|
||||
ino_t :: haiku.ino_t
|
||||
mode_t :: haiku.mode_t
|
||||
nlink_t :: haiku.nlink_t
|
||||
uid_t :: haiku.uid_t
|
||||
gid_t :: haiku.gid_t
|
||||
blksize_t :: haiku.blksize_t
|
||||
blkcnt_t :: haiku.blkcnt_t
|
||||
time_t :: haiku.time_t
|
||||
|
||||
|
||||
Unix_File_Time :: struct {
|
||||
seconds: time_t,
|
||||
nanoseconds: c.long,
|
||||
}
|
||||
|
||||
OS_Stat :: struct {
|
||||
device_id: dev_t, // device ID that this file resides on
|
||||
serial: ino_t, // this file's serial inode ID
|
||||
mode: mode_t, // file mode (rwx for user, group, etc)
|
||||
nlink: nlink_t, // number of hard links to this file
|
||||
uid: uid_t, // user ID of the file's owner
|
||||
gid: gid_t, // group ID of the file's group
|
||||
size: off_t, // file size, in bytes
|
||||
rdev: dev_t, // device type (not used)
|
||||
block_size: blksize_t, // optimal blocksize for I/O
|
||||
|
||||
last_access: Unix_File_Time, // time of last access
|
||||
modified: Unix_File_Time, // time of last data modification
|
||||
status_change: Unix_File_Time, // time of last file status change
|
||||
birthtime: Unix_File_Time, // time of file creation
|
||||
|
||||
type: u32, // attribute/index type
|
||||
|
||||
blocks: blkcnt_t, // blocks allocated for file
|
||||
}
|
||||
|
||||
/* file access modes for open() */
|
||||
O_RDONLY :: 0x0000 /* read only */
|
||||
O_WRONLY :: 0x0001 /* write only */
|
||||
O_RDWR :: 0x0002 /* read and write */
|
||||
O_ACCMODE :: 0x0003 /* mask to get the access modes above */
|
||||
O_RWMASK :: O_ACCMODE
|
||||
|
||||
/* flags for open() */
|
||||
O_EXCL :: 0x0100 /* exclusive creat */
|
||||
O_CREATE :: 0x0200 /* create and open file */
|
||||
O_TRUNC :: 0x0400 /* open with truncation */
|
||||
O_NOCTTY :: 0x1000 /* don't make tty the controlling tty */
|
||||
O_NOTRAVERSE :: 0x2000 /* do not traverse leaf link */
|
||||
|
||||
// File type
|
||||
S_IFMT :: 0o170000 // Type of file mask
|
||||
S_IFIFO :: 0o010000 // Named pipe (fifo)
|
||||
S_IFCHR :: 0o020000 // Character special
|
||||
S_IFDIR :: 0o040000 // Directory
|
||||
S_IFBLK :: 0o060000 // Block special
|
||||
S_IFREG :: 0o100000 // Regular
|
||||
S_IFLNK :: 0o120000 // Symbolic link
|
||||
S_IFSOCK :: 0o140000 // Socket
|
||||
S_ISVTX :: 0o001000 // Save swapped text even after use
|
||||
|
||||
// File mode
|
||||
// Read, write, execute/search by owner
|
||||
S_IRWXU :: 0o0700 // RWX mask for owner
|
||||
S_IRUSR :: 0o0400 // R for owner
|
||||
S_IWUSR :: 0o0200 // W for owner
|
||||
S_IXUSR :: 0o0100 // X for owner
|
||||
|
||||
// Read, write, execute/search by group
|
||||
S_IRWXG :: 0o0070 // RWX mask for group
|
||||
S_IRGRP :: 0o0040 // R for group
|
||||
S_IWGRP :: 0o0020 // W for group
|
||||
S_IXGRP :: 0o0010 // X for group
|
||||
|
||||
// Read, write, execute/search by others
|
||||
S_IRWXO :: 0o0007 // RWX mask for other
|
||||
S_IROTH :: 0o0004 // R for other
|
||||
S_IWOTH :: 0o0002 // W for other
|
||||
S_IXOTH :: 0o0001 // X for other
|
||||
|
||||
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 }
|
||||
|
||||
|
||||
foreign libc {
|
||||
@(link_name="_errnop") __error :: proc() -> ^c.int ---
|
||||
|
||||
@(link_name="fork") _unix_fork :: proc() -> pid_t ---
|
||||
@(link_name="getthrid") _unix_getthrid :: proc() -> 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 ---
|
||||
@(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
|
||||
@(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---
|
||||
@(link_name="lseek") _unix_seek :: proc(fd: Handle, offset: off_t, whence: c.int) -> off_t ---
|
||||
@(link_name="stat") _unix_stat :: proc(path: cstring, sb: ^OS_Stat) -> c.int ---
|
||||
@(link_name="fstat") _unix_fstat :: proc(fd: Handle, sb: ^OS_Stat) -> c.int ---
|
||||
@(link_name="lstat") _unix_lstat :: proc(path: cstring, sb: ^OS_Stat) -> c.int ---
|
||||
@(link_name="readlink") _unix_readlink :: proc(path: cstring, buf: ^byte, bufsiz: c.size_t) -> c.ssize_t ---
|
||||
@(link_name="access") _unix_access :: proc(path: cstring, mask: c.int) -> c.int ---
|
||||
@(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring ---
|
||||
@(link_name="chdir") _unix_chdir :: proc(path: cstring) -> c.int ---
|
||||
@(link_name="rename") _unix_rename :: proc(old, new: cstring) -> c.int ---
|
||||
@(link_name="unlink") _unix_unlink :: proc(path: cstring) -> c.int ---
|
||||
@(link_name="rmdir") _unix_rmdir :: proc(path: cstring) -> c.int ---
|
||||
@(link_name="mkdir") _unix_mkdir :: proc(path: cstring, mode: mode_t) -> c.int ---
|
||||
|
||||
@(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int ---
|
||||
@(link_name="sysconf") _sysconf :: proc(name: c.int) -> c.long ---
|
||||
@(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir ---
|
||||
@(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int ---
|
||||
@(link_name="rewinddir") _unix_rewinddir :: proc(dirp: Dir) ---
|
||||
@(link_name="readdir_r") _unix_readdir_r :: proc(dirp: Dir, entry: ^Dirent, result: ^^Dirent) -> c.int ---
|
||||
|
||||
@(link_name="malloc") _unix_malloc :: proc(size: c.size_t) -> rawptr ---
|
||||
@(link_name="calloc") _unix_calloc :: proc(num, size: c.size_t) -> rawptr ---
|
||||
@(link_name="free") _unix_free :: proc(ptr: rawptr) ---
|
||||
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---
|
||||
|
||||
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---
|
||||
@(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
|
||||
|
||||
@(link_name="exit") _unix_exit :: proc(status: c.int) -> ! ---
|
||||
|
||||
@(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: c.int) -> rawptr ---
|
||||
@(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr ---
|
||||
@(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> c.int ---
|
||||
@(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---
|
||||
}
|
||||
|
||||
MAXNAMLEN :: haiku.NAME_MAX
|
||||
|
||||
Dirent :: struct {
|
||||
dev: dev_t,
|
||||
pdef: dev_t,
|
||||
ino: ino_t,
|
||||
pino: ino_t,
|
||||
reclen: u16,
|
||||
name: [MAXNAMLEN + 1]byte, // name
|
||||
}
|
||||
|
||||
Dir :: distinct rawptr // DIR*
|
||||
|
||||
is_path_separator :: proc(r: rune) -> bool {
|
||||
return r == '/'
|
||||
}
|
||||
|
||||
get_last_error :: proc "contextless" () -> int {
|
||||
return int(__error()^)
|
||||
}
|
||||
|
||||
fork :: proc() -> (Pid, Errno) {
|
||||
pid := _unix_fork()
|
||||
if pid == -1 {
|
||||
return Pid(-1), Errno(get_last_error())
|
||||
}
|
||||
return Pid(pid), ERROR_NONE
|
||||
}
|
||||
|
||||
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
|
||||
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 handle, ERROR_NONE
|
||||
}
|
||||
|
||||
close :: proc(fd: Handle) -> Errno {
|
||||
result := _unix_close(fd)
|
||||
if result == -1 {
|
||||
return Errno(get_last_error())
|
||||
}
|
||||
return ERROR_NONE
|
||||
}
|
||||
|
||||
// In practice a read/write call would probably never read/write these big buffers all at once,
|
||||
// which is why the number of bytes is returned and why there are procs that will call this in a
|
||||
// loop for you.
|
||||
// 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) {
|
||||
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 int(bytes_read), ERROR_NONE
|
||||
}
|
||||
|
||||
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||
if len(data) == 0 {
|
||||
return 0, ERROR_NONE
|
||||
}
|
||||
|
||||
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 int(bytes_written), ERROR_NONE
|
||||
}
|
||||
|
||||
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
|
||||
res := _unix_seek(fd, offset, c.int(whence))
|
||||
if res == -1 {
|
||||
return -1, Errno(get_last_error())
|
||||
}
|
||||
return res, ERROR_NONE
|
||||
}
|
||||
|
||||
file_size :: proc(fd: Handle) -> (i64, Errno) {
|
||||
s, err := _fstat(fd)
|
||||
if err != ERROR_NONE {
|
||||
return -1, err
|
||||
}
|
||||
return s.size, ERROR_NONE
|
||||
}
|
||||
|
||||
// "Argv" arguments converted to Odin strings
|
||||
args := _alloc_command_line_arguments()
|
||||
|
||||
_alloc_command_line_arguments :: proc() -> []string {
|
||||
res := make([]string, len(runtime.args__))
|
||||
for arg, i in runtime.args__ {
|
||||
res[i] = string(arg)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
@private
|
||||
_stat :: proc(path: string) -> (OS_Stat, Errno) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
|
||||
// deliberately uninitialized
|
||||
s: OS_Stat = ---
|
||||
res := _unix_stat(cstr, &s)
|
||||
if res == -1 {
|
||||
return s, Errno(get_last_error())
|
||||
}
|
||||
return s, ERROR_NONE
|
||||
}
|
||||
|
||||
@private
|
||||
_lstat :: proc(path: string) -> (OS_Stat, Errno) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
|
||||
// deliberately uninitialized
|
||||
s: OS_Stat = ---
|
||||
res := _unix_lstat(cstr, &s)
|
||||
if res == -1 {
|
||||
return s, Errno(get_last_error())
|
||||
}
|
||||
return s, ERROR_NONE
|
||||
}
|
||||
|
||||
@private
|
||||
_fstat :: proc(fd: Handle) -> (OS_Stat, Errno) {
|
||||
// deliberately uninitialized
|
||||
s: OS_Stat = ---
|
||||
res := _unix_fstat(fd, &s)
|
||||
if res == -1 {
|
||||
return s, Errno(get_last_error())
|
||||
}
|
||||
return s, ERROR_NONE
|
||||
}
|
||||
|
||||
@private
|
||||
_fdopendir :: proc(fd: Handle) -> (Dir, Errno) {
|
||||
dirp := _unix_fdopendir(fd)
|
||||
if dirp == cast(Dir)nil {
|
||||
return nil, Errno(get_last_error())
|
||||
}
|
||||
return dirp, ERROR_NONE
|
||||
}
|
||||
|
||||
@private
|
||||
_closedir :: proc(dirp: Dir) -> Errno {
|
||||
rc := _unix_closedir(dirp)
|
||||
if rc != 0 {
|
||||
return Errno(get_last_error())
|
||||
}
|
||||
return ERROR_NONE
|
||||
}
|
||||
|
||||
@private
|
||||
_rewinddir :: proc(dirp: Dir) {
|
||||
_unix_rewinddir(dirp)
|
||||
}
|
||||
|
||||
@private
|
||||
_readdir :: proc(dirp: Dir) -> (entry: Dirent, err: Errno, end_of_stream: bool) {
|
||||
result: ^Dirent
|
||||
rc := _unix_readdir_r(dirp, &entry, &result)
|
||||
|
||||
if rc != 0 {
|
||||
err = Errno(get_last_error())
|
||||
return
|
||||
}
|
||||
err = ERROR_NONE
|
||||
|
||||
if result == nil {
|
||||
end_of_stream = true
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@private
|
||||
_readlink :: proc(path: string) -> (string, Errno) {
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
|
||||
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
|
||||
bufsz : uint = MAX_PATH
|
||||
buf := make([]byte, MAX_PATH)
|
||||
for {
|
||||
rc := _unix_readlink(path_cstr, &(buf[0]), bufsz)
|
||||
if rc == -1 {
|
||||
delete(buf)
|
||||
return "", Errno(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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) {
|
||||
return "", Errno(ENOSYS)
|
||||
}
|
||||
|
||||
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
|
||||
rel := rel
|
||||
if rel == "" {
|
||||
rel = "."
|
||||
}
|
||||
|
||||
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(ignore = context.temp_allocator == context.allocator)
|
||||
rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator)
|
||||
|
||||
path_ptr := _unix_realpath(rel_cstr, nil)
|
||||
if path_ptr == nil {
|
||||
return "", Errno(get_last_error())
|
||||
}
|
||||
defer _unix_free(path_ptr)
|
||||
|
||||
path_cstr := transmute(cstring)path_ptr
|
||||
path = strings.clone( string(path_cstr) )
|
||||
|
||||
return path, ERROR_NONE
|
||||
}
|
||||
|
||||
access :: proc(path: string, mask: int) -> (bool, Errno) {
|
||||
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 true, ERROR_NONE
|
||||
}
|
||||
|
||||
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)
|
||||
cstr := _unix_getenv(path_str)
|
||||
if cstr == nil {
|
||||
return "", false
|
||||
}
|
||||
return strings.clone(string(cstr), allocator), true
|
||||
}
|
||||
|
||||
get_env :: proc(key: string, allocator := context.allocator) -> (value: string) {
|
||||
value, _ = lookup_env(key, allocator)
|
||||
return
|
||||
}
|
||||
|
||||
@(private)
|
||||
_processor_core_count :: proc() -> int {
|
||||
info: haiku.system_info
|
||||
haiku.get_system_info(&info)
|
||||
return int(info.cpu_count)
|
||||
}
|
||||
|
||||
exit :: proc "contextless" (code: int) -> ! {
|
||||
runtime._cleanup_runtime_contextless()
|
||||
_unix_exit(i32(code))
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
//+build linux, darwin, freebsd, openbsd
|
||||
//+build linux, darwin, freebsd, openbsd, haiku
|
||||
package os
|
||||
|
||||
import "core:time"
|
||||
|
||||
@@ -32,7 +32,7 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte,
|
||||
}
|
||||
|
||||
case .Read_At:
|
||||
when !(ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD) {
|
||||
when !(ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Haiku) {
|
||||
n_int, os_err = read_at(fd, p, offset)
|
||||
n = i64(n_int)
|
||||
if n == 0 && os_err == 0 {
|
||||
@@ -46,7 +46,7 @@ _file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte,
|
||||
err = .EOF
|
||||
}
|
||||
case .Write_At:
|
||||
when !(ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD) {
|
||||
when !(ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Haiku) {
|
||||
n_int, os_err = write_at(fd, p, offset)
|
||||
n = i64(n_int)
|
||||
if n == 0 && os_err == 0 {
|
||||
@@ -60,7 +60,7 @@ _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 {
|
||||
when ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD || 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})
|
||||
|
||||
167
core/sync/futex_haiku.odin
Normal file
167
core/sync/futex_haiku.odin
Normal file
@@ -0,0 +1,167 @@
|
||||
//+private
|
||||
package sync
|
||||
|
||||
import "core:c"
|
||||
import "core:runtime"
|
||||
import "core:sys/haiku"
|
||||
import "core:sys/unix"
|
||||
import "core:time"
|
||||
|
||||
@(private="file")
|
||||
Wait_Node :: struct {
|
||||
thread: unix.pthread_t,
|
||||
futex: ^Futex,
|
||||
prev, next: ^Wait_Node,
|
||||
}
|
||||
@(private="file")
|
||||
atomic_flag :: distinct bool
|
||||
@(private="file")
|
||||
Wait_Queue :: struct {
|
||||
lock: atomic_flag,
|
||||
list: Wait_Node,
|
||||
}
|
||||
@(private="file")
|
||||
waitq_lock :: proc "contextless" (waitq: ^Wait_Queue) {
|
||||
for cast(bool)atomic_exchange_explicit(&waitq.lock, atomic_flag(true), .Acquire) {
|
||||
cpu_relax() // spin...
|
||||
}
|
||||
}
|
||||
@(private="file")
|
||||
waitq_unlock :: proc "contextless" (waitq: ^Wait_Queue) {
|
||||
atomic_store_explicit(&waitq.lock, atomic_flag(false), .Release)
|
||||
}
|
||||
|
||||
// FIXME: This approach may scale badly in the future,
|
||||
// possible solution - hash map (leads to deadlocks now).
|
||||
@(private="file")
|
||||
g_waitq: Wait_Queue
|
||||
|
||||
@(init, private="file")
|
||||
g_waitq_init :: proc() {
|
||||
g_waitq = {
|
||||
list = {
|
||||
prev = &g_waitq.list,
|
||||
next = &g_waitq.list,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
get_waitq :: #force_inline proc "contextless" (f: ^Futex) -> ^Wait_Queue {
|
||||
_ = f
|
||||
return &g_waitq
|
||||
}
|
||||
|
||||
_futex_wait :: proc "contextless" (f: ^Futex, expect: u32) -> (ok: bool) {
|
||||
waitq := get_waitq(f)
|
||||
waitq_lock(waitq)
|
||||
defer waitq_unlock(waitq)
|
||||
|
||||
head := &waitq.list
|
||||
waiter := Wait_Node{
|
||||
thread = unix.pthread_self(),
|
||||
futex = f,
|
||||
prev = head,
|
||||
next = head.next,
|
||||
}
|
||||
|
||||
waiter.prev.next = &waiter
|
||||
waiter.next.prev = &waiter
|
||||
|
||||
old_mask, mask: haiku.sigset_t
|
||||
haiku.sigemptyset(&mask)
|
||||
haiku.sigaddset(&mask, haiku.SIGCONT)
|
||||
unix.pthread_sigmask(haiku.SIG_BLOCK, &mask, &old_mask)
|
||||
|
||||
if u32(atomic_load_explicit(f, .Acquire)) == expect {
|
||||
waitq_unlock(waitq)
|
||||
defer waitq_lock(waitq)
|
||||
|
||||
sig: c.int
|
||||
haiku.sigwait(&mask, &sig)
|
||||
errno := haiku.errno()
|
||||
ok = errno == .OK
|
||||
}
|
||||
|
||||
waiter.prev.next = waiter.next
|
||||
waiter.next.prev = waiter.prev
|
||||
|
||||
unix.pthread_sigmask(haiku.SIG_SETMASK, &old_mask, nil)
|
||||
|
||||
// FIXME: Add error handling!
|
||||
return
|
||||
}
|
||||
|
||||
_futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expect: u32, duration: time.Duration) -> (ok: bool) {
|
||||
if duration <= 0 {
|
||||
return false
|
||||
}
|
||||
waitq := get_waitq(f)
|
||||
waitq_lock(waitq)
|
||||
defer waitq_unlock(waitq)
|
||||
|
||||
head := &waitq.list
|
||||
waiter := Wait_Node{
|
||||
thread = unix.pthread_self(),
|
||||
futex = f,
|
||||
prev = head,
|
||||
next = head.next,
|
||||
}
|
||||
|
||||
waiter.prev.next = &waiter
|
||||
waiter.next.prev = &waiter
|
||||
|
||||
old_mask, mask: haiku.sigset_t
|
||||
haiku.sigemptyset(&mask)
|
||||
haiku.sigaddset(&mask, haiku.SIGCONT)
|
||||
unix.pthread_sigmask(haiku.SIG_BLOCK, &mask, &old_mask)
|
||||
|
||||
if u32(atomic_load_explicit(f, .Acquire)) == expect {
|
||||
waitq_unlock(waitq)
|
||||
defer waitq_lock(waitq)
|
||||
|
||||
info: haiku.siginfo_t
|
||||
ts := unix.timespec{
|
||||
tv_sec = i64(duration / 1e9),
|
||||
tv_nsec = i64(duration % 1e9),
|
||||
}
|
||||
haiku.sigtimedwait(&mask, &info, &ts)
|
||||
errno := haiku.errno()
|
||||
ok = errno == .EAGAIN || errno == .OK
|
||||
}
|
||||
|
||||
waiter.prev.next = waiter.next
|
||||
waiter.next.prev = waiter.prev
|
||||
|
||||
unix.pthread_sigmask(haiku.SIG_SETMASK, &old_mask, nil)
|
||||
|
||||
// FIXME: Add error handling!
|
||||
return
|
||||
}
|
||||
|
||||
_futex_signal :: proc "contextless" (f: ^Futex) {
|
||||
waitq := get_waitq(f)
|
||||
waitq_lock(waitq)
|
||||
defer waitq_unlock(waitq)
|
||||
|
||||
head := &waitq.list
|
||||
for waiter := head.next; waiter != head; waiter = waiter.next {
|
||||
if waiter.futex == f {
|
||||
unix.pthread_kill(waiter.thread, haiku.SIGCONT)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_futex_broadcast :: proc "contextless" (f: ^Futex) {
|
||||
waitq := get_waitq(f)
|
||||
waitq_lock(waitq)
|
||||
defer waitq_unlock(waitq)
|
||||
|
||||
head := &waitq.list
|
||||
for waiter := head.next; waiter != head; waiter = waiter.next {
|
||||
if waiter.futex == f {
|
||||
unix.pthread_kill(waiter.thread, haiku.SIGCONT)
|
||||
}
|
||||
}
|
||||
}
|
||||
8
core/sync/primitives_haiku.odin
Normal file
8
core/sync/primitives_haiku.odin
Normal file
@@ -0,0 +1,8 @@
|
||||
//+private
|
||||
package sync
|
||||
|
||||
import "core:sys/haiku"
|
||||
|
||||
_current_thread_id :: proc "contextless" () -> int {
|
||||
return int(haiku.find_thread(nil))
|
||||
}
|
||||
239
core/sys/haiku/errors.odin
Normal file
239
core/sys/haiku/errors.odin
Normal file
@@ -0,0 +1,239 @@
|
||||
//+build haiku
|
||||
package sys_haiku
|
||||
|
||||
import "core:c"
|
||||
|
||||
Errno :: enum c.int {
|
||||
// Error baselines
|
||||
GENERAL_ERROR_BASE = min(c.int),
|
||||
OS_ERROR_BASE = GENERAL_ERROR_BASE + 0x1000,
|
||||
APP_ERROR_BASE = GENERAL_ERROR_BASE + 0x2000,
|
||||
INTERFACE_ERROR_BASE = GENERAL_ERROR_BASE + 0x3000,
|
||||
MEDIA_ERROR_BASE = GENERAL_ERROR_BASE + 0x4000,
|
||||
TRANSLATION_ERROR_BASE = GENERAL_ERROR_BASE + 0x4800,
|
||||
MIDI_ERROR_BASE = GENERAL_ERROR_BASE + 0x5000,
|
||||
STORAGE_ERROR_BASE = GENERAL_ERROR_BASE + 0x6000,
|
||||
POSIX_ERROR_BASE = GENERAL_ERROR_BASE + 0x7000,
|
||||
MAIL_ERROR_BASE = GENERAL_ERROR_BASE + 0x8000,
|
||||
PRINT_ERROR_BASE = GENERAL_ERROR_BASE + 0x9000,
|
||||
DEVICE_ERROR_BASE = GENERAL_ERROR_BASE + 0xa000,
|
||||
|
||||
// Developer-defined errors start at (ERRORS_END+1)
|
||||
ERRORS_END = GENERAL_ERROR_BASE + 0xffff,
|
||||
|
||||
// General Errors
|
||||
NO_MEMORY = GENERAL_ERROR_BASE + 0,
|
||||
IO_ERROR = GENERAL_ERROR_BASE + 1,
|
||||
PERMISSION_DENIED = GENERAL_ERROR_BASE + 2,
|
||||
BAD_INDEX = GENERAL_ERROR_BASE + 3,
|
||||
BAD_TYPE = GENERAL_ERROR_BASE + 4,
|
||||
BAD_VALUE = GENERAL_ERROR_BASE + 5,
|
||||
MISMATCHED_VALUES = GENERAL_ERROR_BASE + 6,
|
||||
NAME_NOT_FOUND = GENERAL_ERROR_BASE + 7,
|
||||
NAME_IN_USE = GENERAL_ERROR_BASE + 8,
|
||||
TIMED_OUT = GENERAL_ERROR_BASE + 9,
|
||||
INTERRUPTED = GENERAL_ERROR_BASE + 10,
|
||||
WOULD_BLOCK = GENERAL_ERROR_BASE + 11,
|
||||
CANCELED = GENERAL_ERROR_BASE + 12,
|
||||
NO_INIT = GENERAL_ERROR_BASE + 13,
|
||||
NOT_INITIALIZED = GENERAL_ERROR_BASE + 13,
|
||||
BUSY = GENERAL_ERROR_BASE + 14,
|
||||
NOT_ALLOWED = GENERAL_ERROR_BASE + 15,
|
||||
BAD_DATA = GENERAL_ERROR_BASE + 16,
|
||||
DONT_DO_THAT = GENERAL_ERROR_BASE + 17,
|
||||
|
||||
ERROR = -1,
|
||||
OK = 0,
|
||||
NO_ERROR = 0,
|
||||
|
||||
// Kernel Kit Errors
|
||||
BAD_SEM_ID = OS_ERROR_BASE + 0,
|
||||
NO_MORE_SEMS = OS_ERROR_BASE + 1,
|
||||
BAD_THREAD_ID = OS_ERROR_BASE + 0x100,
|
||||
NO_MORE_THREADS = OS_ERROR_BASE + 0x101,
|
||||
BAD_THREAD_STATE = OS_ERROR_BASE + 0x102,
|
||||
BAD_TEAM_ID = OS_ERROR_BASE + 0x103,
|
||||
NO_MORE_TEAMS = OS_ERROR_BASE + 0x104,
|
||||
BAD_PORT_ID = OS_ERROR_BASE + 0x200,
|
||||
NO_MORE_PORTS = OS_ERROR_BASE + 0x201,
|
||||
BAD_IMAGE_ID = OS_ERROR_BASE + 0x300,
|
||||
BAD_ADDRESS = OS_ERROR_BASE + 0x301,
|
||||
NOT_AN_EXECUTABLE = OS_ERROR_BASE + 0x302,
|
||||
MISSING_LIBRARY = OS_ERROR_BASE + 0x303,
|
||||
MISSING_SYMBOL = OS_ERROR_BASE + 0x304,
|
||||
UNKNOWN_EXECUTABLE = OS_ERROR_BASE + 0x305,
|
||||
LEGACY_EXECUTABLE = OS_ERROR_BASE + 0x306,
|
||||
|
||||
DEBUGGER_ALREADY_INSTALLED = OS_ERROR_BASE + 0x400,
|
||||
|
||||
// Application Kit Errors
|
||||
BAD_REPLY = APP_ERROR_BASE + 0,
|
||||
DUPLICATE_REPLY = APP_ERROR_BASE + 1,
|
||||
MESSAGE_TO_SELF = APP_ERROR_BASE + 2,
|
||||
BAD_HANDLER = APP_ERROR_BASE + 3,
|
||||
ALREADY_RUNNING = APP_ERROR_BASE + 4,
|
||||
LAUNCH_FAILED = APP_ERROR_BASE + 5,
|
||||
AMBIGUOUS_APP_LAUNCH = APP_ERROR_BASE + 6,
|
||||
UNKNOWN_MIME_TYPE = APP_ERROR_BASE + 7,
|
||||
BAD_SCRIPT_SYNTAX = APP_ERROR_BASE + 8,
|
||||
LAUNCH_FAILED_NO_RESOLVE_LINK = APP_ERROR_BASE + 9,
|
||||
LAUNCH_FAILED_EXECUTABLE = APP_ERROR_BASE + 10,
|
||||
LAUNCH_FAILED_APP_NOT_FOUND = APP_ERROR_BASE + 11,
|
||||
LAUNCH_FAILED_APP_IN_TRASH = APP_ERROR_BASE + 12,
|
||||
LAUNCH_FAILED_NO_PREFERRED_APP = APP_ERROR_BASE + 13,
|
||||
LAUNCH_FAILED_FILES_APP_NOT_FOUND = APP_ERROR_BASE + 14,
|
||||
BAD_MIME_SNIFFER_RULE = APP_ERROR_BASE + 15,
|
||||
NOT_A_MESSAGE = APP_ERROR_BASE + 16,
|
||||
SHUTDOWN_CANCELLED = APP_ERROR_BASE + 17,
|
||||
SHUTTING_DOWN = APP_ERROR_BASE + 18,
|
||||
|
||||
// Storage Kit/File System Errors
|
||||
FILE_ERROR = STORAGE_ERROR_BASE + 0,
|
||||
// 1 was B_FILE_NOT_FOUND (deprecated)
|
||||
FILE_EXISTS = STORAGE_ERROR_BASE + 2,
|
||||
ENTRY_NOT_FOUND = STORAGE_ERROR_BASE + 3,
|
||||
NAME_TOO_LONG = STORAGE_ERROR_BASE + 4,
|
||||
NOT_A_DIRECTORY = STORAGE_ERROR_BASE + 5,
|
||||
DIRECTORY_NOT_EMPTY = STORAGE_ERROR_BASE + 6,
|
||||
DEVICE_FULL = STORAGE_ERROR_BASE + 7,
|
||||
READ_ONLY_DEVICE = STORAGE_ERROR_BASE + 8,
|
||||
IS_A_DIRECTORY = STORAGE_ERROR_BASE + 9,
|
||||
NO_MORE_FDS = STORAGE_ERROR_BASE + 10,
|
||||
CROSS_DEVICE_LINK = STORAGE_ERROR_BASE + 11,
|
||||
LINK_LIMIT = STORAGE_ERROR_BASE + 12,
|
||||
BUSTED_PIPE = STORAGE_ERROR_BASE + 13,
|
||||
UNSUPPORTED = STORAGE_ERROR_BASE + 14,
|
||||
PARTITION_TOO_SMALL = STORAGE_ERROR_BASE + 15,
|
||||
PARTIAL_READ = STORAGE_ERROR_BASE + 16,
|
||||
PARTIAL_WRITE = STORAGE_ERROR_BASE + 17,
|
||||
|
||||
// Some POSIX errors
|
||||
E2BIG = POSIX_ERROR_BASE + 1,
|
||||
EFBIG = POSIX_ERROR_BASE + 4,
|
||||
ENODEV = POSIX_ERROR_BASE + 7,
|
||||
ERANGE = POSIX_ERROR_BASE + 17,
|
||||
EOVERFLOW = POSIX_ERROR_BASE + 41,
|
||||
EOPNOTSUPP = POSIX_ERROR_BASE + 43,
|
||||
|
||||
ENOSYS = POSIX_ERROR_BASE + 9,
|
||||
EAGAIN = WOULD_BLOCK,
|
||||
|
||||
// New error codes that can be mapped to POSIX errors
|
||||
TOO_MANY_ARGS_NEG = E2BIG,
|
||||
FILE_TOO_LARGE_NEG = EFBIG,
|
||||
DEVICE_NOT_FOUND_NEG = ENODEV,
|
||||
RESULT_NOT_REPRESENTABLE_NEG = ERANGE,
|
||||
BUFFER_OVERFLOW_NEG = EOVERFLOW,
|
||||
NOT_SUPPORTED_NEG = EOPNOTSUPP,
|
||||
|
||||
TOO_MANY_ARGS_POS = -E2BIG,
|
||||
FILE_TOO_LARGE_POS = -EFBIG,
|
||||
DEVICE_NOT_FOUND_POS = -ENODEV,
|
||||
RESULT_NOT_REPRESENTABLE_POS = -ERANGE,
|
||||
BUFFER_OVERFLOW_POS = -EOVERFLOW,
|
||||
NOT_SUPPORTED_POS = -EOPNOTSUPP,
|
||||
|
||||
// Media Kit Errors
|
||||
STREAM_NOT_FOUND = MEDIA_ERROR_BASE + 0,
|
||||
SERVER_NOT_FOUND = MEDIA_ERROR_BASE + 1,
|
||||
RESOURCE_NOT_FOUND = MEDIA_ERROR_BASE + 2,
|
||||
RESOURCE_UNAVAILABLE = MEDIA_ERROR_BASE + 3,
|
||||
BAD_SUBSCRIBER = MEDIA_ERROR_BASE + 4,
|
||||
SUBSCRIBER_NOT_ENTERED = MEDIA_ERROR_BASE + 5,
|
||||
BUFFER_NOT_AVAILABLE = MEDIA_ERROR_BASE + 6,
|
||||
LAST_BUFFER_ERROR = MEDIA_ERROR_BASE + 7,
|
||||
MEDIA_SYSTEM_FAILURE = MEDIA_ERROR_BASE + 100,
|
||||
MEDIA_BAD_NODE = MEDIA_ERROR_BASE + 101,
|
||||
MEDIA_NODE_BUSY = MEDIA_ERROR_BASE + 102,
|
||||
MEDIA_BAD_FORMAT = MEDIA_ERROR_BASE + 103,
|
||||
MEDIA_BAD_BUFFER = MEDIA_ERROR_BASE + 104,
|
||||
MEDIA_TOO_MANY_NODES = MEDIA_ERROR_BASE + 105,
|
||||
MEDIA_TOO_MANY_BUFFERS = MEDIA_ERROR_BASE + 106,
|
||||
MEDIA_NODE_ALREADY_EXISTS = MEDIA_ERROR_BASE + 107,
|
||||
MEDIA_BUFFER_ALREADY_EXISTS = MEDIA_ERROR_BASE + 108,
|
||||
MEDIA_CANNOT_SEEK = MEDIA_ERROR_BASE + 109,
|
||||
MEDIA_CANNOT_CHANGE_RUN_MODE = MEDIA_ERROR_BASE + 110,
|
||||
MEDIA_APP_ALREADY_REGISTERED = MEDIA_ERROR_BASE + 111,
|
||||
MEDIA_APP_NOT_REGISTERED = MEDIA_ERROR_BASE + 112,
|
||||
MEDIA_CANNOT_RECLAIM_BUFFERS = MEDIA_ERROR_BASE + 113,
|
||||
MEDIA_BUFFERS_NOT_RECLAIMED = MEDIA_ERROR_BASE + 114,
|
||||
MEDIA_TIME_SOURCE_STOPPED = MEDIA_ERROR_BASE + 115,
|
||||
MEDIA_TIME_SOURCE_BUSY = MEDIA_ERROR_BASE + 116,
|
||||
MEDIA_BAD_SOURCE = MEDIA_ERROR_BASE + 117,
|
||||
MEDIA_BAD_DESTINATION = MEDIA_ERROR_BASE + 118,
|
||||
MEDIA_ALREADY_CONNECTED = MEDIA_ERROR_BASE + 119,
|
||||
MEDIA_NOT_CONNECTED = MEDIA_ERROR_BASE + 120,
|
||||
MEDIA_BAD_CLIP_FORMAT = MEDIA_ERROR_BASE + 121,
|
||||
MEDIA_ADDON_FAILED = MEDIA_ERROR_BASE + 122,
|
||||
MEDIA_ADDON_DISABLED = MEDIA_ERROR_BASE + 123,
|
||||
MEDIA_CHANGE_IN_PROGRESS = MEDIA_ERROR_BASE + 124,
|
||||
MEDIA_STALE_CHANGE_COUNT = MEDIA_ERROR_BASE + 125,
|
||||
MEDIA_ADDON_RESTRICTED = MEDIA_ERROR_BASE + 126,
|
||||
MEDIA_NO_HANDLER = MEDIA_ERROR_BASE + 127,
|
||||
MEDIA_DUPLICATE_FORMAT = MEDIA_ERROR_BASE + 128,
|
||||
MEDIA_REALTIME_DISABLED = MEDIA_ERROR_BASE + 129,
|
||||
MEDIA_REALTIME_UNAVAILABLE = MEDIA_ERROR_BASE + 130,
|
||||
|
||||
// Mail Kit Errors
|
||||
MAIL_NO_DAEMON = MAIL_ERROR_BASE + 0,
|
||||
MAIL_UNKNOWN_USER = MAIL_ERROR_BASE + 1,
|
||||
MAIL_WRONG_PASSWORD = MAIL_ERROR_BASE + 2,
|
||||
MAIL_UNKNOWN_HOST = MAIL_ERROR_BASE + 3,
|
||||
MAIL_ACCESS_ERROR = MAIL_ERROR_BASE + 4,
|
||||
MAIL_UNKNOWN_FIELD = MAIL_ERROR_BASE + 5,
|
||||
MAIL_NO_RECIPIENT = MAIL_ERROR_BASE + 6,
|
||||
MAIL_INVALID_MAIL = MAIL_ERROR_BASE + 7,
|
||||
|
||||
// Printing Errors
|
||||
NO_PRINT_SERVER = PRINT_ERROR_BASE + 0,
|
||||
|
||||
// Device Kit Errors
|
||||
DEV_INVALID_IOCTL = DEVICE_ERROR_BASE + 0,
|
||||
DEV_NO_MEMORY = DEVICE_ERROR_BASE + 1,
|
||||
DEV_BAD_DRIVE_NUM = DEVICE_ERROR_BASE + 2,
|
||||
DEV_NO_MEDIA = DEVICE_ERROR_BASE + 3,
|
||||
DEV_UNREADABLE = DEVICE_ERROR_BASE + 4,
|
||||
DEV_FORMAT_ERROR = DEVICE_ERROR_BASE + 5,
|
||||
DEV_TIMEOUT = DEVICE_ERROR_BASE + 6,
|
||||
DEV_RECALIBRATE_ERROR = DEVICE_ERROR_BASE + 7,
|
||||
DEV_SEEK_ERROR = DEVICE_ERROR_BASE + 8,
|
||||
DEV_ID_ERROR = DEVICE_ERROR_BASE + 9,
|
||||
DEV_READ_ERROR = DEVICE_ERROR_BASE + 10,
|
||||
DEV_WRITE_ERROR = DEVICE_ERROR_BASE + 11,
|
||||
DEV_NOT_READY = DEVICE_ERROR_BASE + 12,
|
||||
DEV_MEDIA_CHANGED = DEVICE_ERROR_BASE + 13,
|
||||
DEV_MEDIA_CHANGE_REQUESTED = DEVICE_ERROR_BASE + 14,
|
||||
DEV_RESOURCE_CONFLICT = DEVICE_ERROR_BASE + 15,
|
||||
DEV_CONFIGURATION_ERROR = DEVICE_ERROR_BASE + 16,
|
||||
DEV_DISABLED_BY_USER = DEVICE_ERROR_BASE + 17,
|
||||
DEV_DOOR_OPEN = DEVICE_ERROR_BASE + 18,
|
||||
DEV_INVALID_PIPE = DEVICE_ERROR_BASE + 19,
|
||||
DEV_CRC_ERROR = DEVICE_ERROR_BASE + 20,
|
||||
DEV_STALLED = DEVICE_ERROR_BASE + 21,
|
||||
DEV_BAD_PID = DEVICE_ERROR_BASE + 22,
|
||||
DEV_UNEXPECTED_PID = DEVICE_ERROR_BASE + 23,
|
||||
DEV_DATA_OVERRUN = DEVICE_ERROR_BASE + 24,
|
||||
DEV_DATA_UNDERRUN = DEVICE_ERROR_BASE + 25,
|
||||
DEV_FIFO_OVERRUN = DEVICE_ERROR_BASE + 26,
|
||||
DEV_FIFO_UNDERRUN = DEVICE_ERROR_BASE + 27,
|
||||
DEV_PENDING = DEVICE_ERROR_BASE + 28,
|
||||
DEV_MULTIPLE_ERRORS = DEVICE_ERROR_BASE + 29,
|
||||
DEV_TOO_LATE = DEVICE_ERROR_BASE + 30,
|
||||
|
||||
// Translation Kit Errors
|
||||
TRANSLATION_BASE_ERROR = TRANSLATION_ERROR_BASE + 0,
|
||||
NO_TRANSLATOR = TRANSLATION_ERROR_BASE + 1,
|
||||
ILLEGAL_DATA = TRANSLATION_ERROR_BASE + 2,
|
||||
}
|
||||
|
||||
errno :: #force_inline proc "contextless" () -> Errno {
|
||||
return Errno(_errnop()^)
|
||||
}
|
||||
|
||||
foreign import libroot "system:c"
|
||||
foreign libroot {
|
||||
_to_positive_error :: proc(error: c.int) -> c.int ---
|
||||
_to_negative_error :: proc(error: c.int) -> c.int ---
|
||||
|
||||
_errnop :: proc() -> ^c.int ---
|
||||
}
|
||||
168
core/sys/haiku/find_directory.odin
Normal file
168
core/sys/haiku/find_directory.odin
Normal file
@@ -0,0 +1,168 @@
|
||||
//+build haiku
|
||||
package sys_haiku
|
||||
|
||||
import "core:c"
|
||||
|
||||
directory_which :: enum c.int {
|
||||
// Per volume directories
|
||||
DESKTOP_DIRECTORY = 0,
|
||||
TRASH_DIRECTORY,
|
||||
|
||||
// System directories
|
||||
SYSTEM_DIRECTORY = 1000,
|
||||
SYSTEM_ADDONS_DIRECTORY = 1002,
|
||||
SYSTEM_BOOT_DIRECTORY,
|
||||
SYSTEM_FONTS_DIRECTORY,
|
||||
SYSTEM_LIB_DIRECTORY,
|
||||
SYSTEM_SERVERS_DIRECTORY,
|
||||
SYSTEM_APPS_DIRECTORY,
|
||||
SYSTEM_BIN_DIRECTORY,
|
||||
SYSTEM_DOCUMENTATION_DIRECTORY = 1010,
|
||||
SYSTEM_PREFERENCES_DIRECTORY,
|
||||
SYSTEM_TRANSLATORS_DIRECTORY,
|
||||
SYSTEM_MEDIA_NODES_DIRECTORY,
|
||||
SYSTEM_SOUNDS_DIRECTORY,
|
||||
SYSTEM_DATA_DIRECTORY,
|
||||
SYSTEM_DEVELOP_DIRECTORY,
|
||||
SYSTEM_PACKAGES_DIRECTORY,
|
||||
SYSTEM_HEADERS_DIRECTORY,
|
||||
SYSTEM_ETC_DIRECTORY = 2008,
|
||||
SYSTEM_SETTINGS_DIRECTORY = 2010,
|
||||
SYSTEM_LOG_DIRECTORY = 2012,
|
||||
SYSTEM_SPOOL_DIRECTORY,
|
||||
SYSTEM_TEMP_DIRECTORY,
|
||||
SYSTEM_VAR_DIRECTORY,
|
||||
SYSTEM_CACHE_DIRECTORY = 2020,
|
||||
SYSTEM_NONPACKAGED_DIRECTORY = 2023,
|
||||
SYSTEM_NONPACKAGED_ADDONS_DIRECTORY,
|
||||
SYSTEM_NONPACKAGED_TRANSLATORS_DIRECTORY,
|
||||
SYSTEM_NONPACKAGED_MEDIA_NODES_DIRECTORY,
|
||||
SYSTEM_NONPACKAGED_BIN_DIRECTORY,
|
||||
SYSTEM_NONPACKAGED_DATA_DIRECTORY,
|
||||
SYSTEM_NONPACKAGED_FONTS_DIRECTORY,
|
||||
SYSTEM_NONPACKAGED_SOUNDS_DIRECTORY,
|
||||
SYSTEM_NONPACKAGED_DOCUMENTATION_DIRECTORY,
|
||||
SYSTEM_NONPACKAGED_LIB_DIRECTORY,
|
||||
SYSTEM_NONPACKAGED_HEADERS_DIRECTORY,
|
||||
SYSTEM_NONPACKAGED_DEVELOP_DIRECTORY,
|
||||
|
||||
// User directories. These are interpreted in the context of the user making the find_directory call.
|
||||
USER_DIRECTORY = 3000,
|
||||
USER_CONFIG_DIRECTORY,
|
||||
USER_ADDONS_DIRECTORY,
|
||||
USER_BOOT_DIRECTORY,
|
||||
USER_FONTS_DIRECTORY,
|
||||
USER_LIB_DIRECTORY,
|
||||
USER_SETTINGS_DIRECTORY,
|
||||
USER_DESKBAR_DIRECTORY,
|
||||
USER_PRINTERS_DIRECTORY,
|
||||
USER_TRANSLATORS_DIRECTORY,
|
||||
USER_MEDIA_NODES_DIRECTORY,
|
||||
USER_SOUNDS_DIRECTORY,
|
||||
USER_DATA_DIRECTORY,
|
||||
USER_CACHE_DIRECTORY,
|
||||
USER_PACKAGES_DIRECTORY,
|
||||
USER_HEADERS_DIRECTORY,
|
||||
USER_NONPACKAGED_DIRECTORY,
|
||||
USER_NONPACKAGED_ADDONS_DIRECTORY,
|
||||
USER_NONPACKAGED_TRANSLATORS_DIRECTORY,
|
||||
USER_NONPACKAGED_MEDIA_NODES_DIRECTORY,
|
||||
USER_NONPACKAGED_BIN_DIRECTORY,
|
||||
USER_NONPACKAGED_DATA_DIRECTORY,
|
||||
USER_NONPACKAGED_FONTS_DIRECTORY,
|
||||
USER_NONPACKAGED_SOUNDS_DIRECTORY,
|
||||
USER_NONPACKAGED_DOCUMENTATION_DIRECTORY,
|
||||
USER_NONPACKAGED_LIB_DIRECTORY,
|
||||
USER_NONPACKAGED_HEADERS_DIRECTORY,
|
||||
USER_NONPACKAGED_DEVELOP_DIRECTORY,
|
||||
USER_DEVELOP_DIRECTORY,
|
||||
USER_DOCUMENTATION_DIRECTORY,
|
||||
USER_SERVERS_DIRECTORY,
|
||||
USER_APPS_DIRECTORY,
|
||||
USER_BIN_DIRECTORY,
|
||||
USER_PREFERENCES_DIRECTORY,
|
||||
USER_ETC_DIRECTORY,
|
||||
USER_LOG_DIRECTORY,
|
||||
USER_SPOOL_DIRECTORY,
|
||||
USER_VAR_DIRECTORY,
|
||||
|
||||
// Global directories
|
||||
APPS_DIRECTORY = 4000,
|
||||
PREFERENCES_DIRECTORY,
|
||||
UTILITIES_DIRECTORY,
|
||||
PACKAGE_LINKS_DIRECTORY,
|
||||
|
||||
// Obsolete: Legacy BeOS definition to be phased out
|
||||
BEOS_DIRECTORY = 1000,
|
||||
BEOS_SYSTEM_DIRECTORY,
|
||||
BEOS_ADDONS_DIRECTORY,
|
||||
BEOS_BOOT_DIRECTORY,
|
||||
BEOS_FONTS_DIRECTORY,
|
||||
BEOS_LIB_DIRECTORY,
|
||||
BEOS_SERVERS_DIRECTORY,
|
||||
BEOS_APPS_DIRECTORY,
|
||||
BEOS_BIN_DIRECTORY,
|
||||
BEOS_ETC_DIRECTORY,
|
||||
BEOS_DOCUMENTATION_DIRECTORY,
|
||||
BEOS_PREFERENCES_DIRECTORY,
|
||||
BEOS_TRANSLATORS_DIRECTORY,
|
||||
BEOS_MEDIA_NODES_DIRECTORY,
|
||||
BEOS_SOUNDS_DIRECTORY,
|
||||
}
|
||||
|
||||
find_path_flags :: enum c.int {
|
||||
CREATE_DIRECTORY = 0x0001,
|
||||
CREATE_PARENT_DIRECTORY = 0x0002,
|
||||
EXISTING_ONLY = 0x0004,
|
||||
|
||||
// find_paths() only!
|
||||
SYSTEM_ONLY = 0x0010,
|
||||
USER_ONLY = 0x0020,
|
||||
}
|
||||
|
||||
path_base_directory :: enum c.int {
|
||||
INSTALLATION_LOCATION_DIRECTORY,
|
||||
ADD_ONS_DIRECTORY,
|
||||
APPS_DIRECTORY,
|
||||
BIN_DIRECTORY,
|
||||
BOOT_DIRECTORY,
|
||||
CACHE_DIRECTORY,
|
||||
DATA_DIRECTORY,
|
||||
DEVELOP_DIRECTORY,
|
||||
DEVELOP_LIB_DIRECTORY,
|
||||
DOCUMENTATION_DIRECTORY,
|
||||
ETC_DIRECTORY,
|
||||
FONTS_DIRECTORY,
|
||||
HEADERS_DIRECTORY,
|
||||
LIB_DIRECTORY,
|
||||
LOG_DIRECTORY,
|
||||
MEDIA_NODES_DIRECTORY,
|
||||
PACKAGES_DIRECTORY,
|
||||
PREFERENCES_DIRECTORY,
|
||||
SERVERS_DIRECTORY,
|
||||
SETTINGS_DIRECTORY,
|
||||
SOUNDS_DIRECTORY,
|
||||
SPOOL_DIRECTORY,
|
||||
TRANSLATORS_DIRECTORY,
|
||||
VAR_DIRECTORY,
|
||||
|
||||
// find_path() only!
|
||||
IMAGE_PATH = 1000,
|
||||
PACKAGE_PATH,
|
||||
}
|
||||
|
||||
// value that can be used instead of a pointer to a symbol in the program image
|
||||
APP_IMAGE_SYMBOL :: rawptr(addr_t(0))
|
||||
// pointer to a symbol in the callers image (same as B_CURRENT_IMAGE_SYMBOL)
|
||||
current_image_symbol :: proc() -> rawptr { return rawptr(current_image_symbol) }
|
||||
|
||||
foreign import libroot "system:c"
|
||||
foreign libroot {
|
||||
find_directory :: proc(which: directory_which, volume: dev_t, createIt: bool, pathString: [^]c.char, length: i32) -> status_t ---
|
||||
find_path :: proc(codePointer: rawptr, baseDirectory: path_base_directory, subPath: cstring, pathBuffer: [^]c.char, bufferSize: c.size_t) -> status_t ---
|
||||
find_path_etc :: proc(codePointer: rawptr, dependency: cstring, architecture: cstring, baseDirectory: path_base_directory, subPath: cstring, flags: find_path_flags, pathBuffer: [^]c.char, bufferSize: c.size_t) -> status_t ---
|
||||
find_path_for_path :: proc(path: cstring, baseDirectory: path_base_directory, subPath: cstring, pathBuffer: [^]c.char, bufferSize: c.size_t) -> status_t ---
|
||||
find_path_for_path_etc :: proc(path: cstring, dependency: cstring, architecture: cstring, baseDirectory: path_base_directory, subPath: cstring, flags: find_path_flags, pathBuffer: [^]c.char, bufferSize: c.size_t) -> status_t ---
|
||||
find_paths :: proc(baseDirectory: path_base_directory, subPath: cstring, _paths: ^[^][^]c.char, _pathCount: ^c.size_t) -> status_t ---
|
||||
find_paths_etc :: proc(architecture: cstring, baseDirectory: path_base_directory, subPath: cstring, flags: find_path_flags, _paths: ^[^][^]c.char, _pathCount: ^c.size_t) -> status_t ---
|
||||
}
|
||||
502
core/sys/haiku/os.odin
Normal file
502
core/sys/haiku/os.odin
Normal file
@@ -0,0 +1,502 @@
|
||||
//+build haiku
|
||||
package sys_haiku
|
||||
|
||||
import "core:c"
|
||||
import "core:sys/unix"
|
||||
|
||||
foreign import libroot "system:c"
|
||||
|
||||
PATH_MAX :: 1024
|
||||
NAME_MAX :: 256
|
||||
MAXPATHLEN :: PATH_MAX
|
||||
|
||||
FILE_NAME_LENGTH :: NAME_MAX
|
||||
PATH_NAME_LENGTH :: MAXPATHLEN
|
||||
OS_NAME_LENGTH :: 32
|
||||
|
||||
// Areas
|
||||
|
||||
area_info :: struct {
|
||||
area: area_id,
|
||||
name: [OS_NAME_LENGTH]c.char,
|
||||
size: c.size_t,
|
||||
lock: u32,
|
||||
protection: u32,
|
||||
team: team_id,
|
||||
ram_size: u32,
|
||||
copy_count: u32,
|
||||
in_count: u32,
|
||||
out_count: u32,
|
||||
address: rawptr,
|
||||
}
|
||||
|
||||
area_locking :: enum u32 {
|
||||
NO_LOCK = 0,
|
||||
LAZY_LOCK = 1,
|
||||
FULL_LOCK = 2,
|
||||
CONTIGUOUS = 3,
|
||||
LOMEM = 4, // CONTIGUOUS, < 16 MB physical address
|
||||
_32_BIT_FULL_LOCK = 5, // FULL_LOCK, < 4 GB physical addresses
|
||||
_32_BIT_CONTIGUOUS = 6, // CONTIGUOUS, < 4 GB physical address
|
||||
}
|
||||
|
||||
// for create_area() and clone_area()
|
||||
address_spec :: enum u32 {
|
||||
ANY_ADDRESS = 0,
|
||||
EXACT_ADDRESS = 1,
|
||||
BASE_ADDRESS = 2,
|
||||
CLONE_ADDRESS = 3,
|
||||
ANY_KERNEL_ADDRESS = 4,
|
||||
// ANY_KERNEL_BLOCK_ADDRESS = 5,
|
||||
RANDOMIZED_ANY_ADDRESS = 6,
|
||||
RANDOMIZED_BASE_ADDRESS = 7,
|
||||
}
|
||||
|
||||
area_protection_flags :: enum u32 {
|
||||
READ_AREA = 1 << 0,
|
||||
WRITE_AREA = 1 << 1,
|
||||
EXECUTE_AREA = 1 << 2,
|
||||
// "stack" protection is not available on most platforms - it's used
|
||||
// to only commit memory as needed, and have guard pages at the
|
||||
// bottom of the stack.
|
||||
STACK_AREA = 1 << 3,
|
||||
CLONEABLE_AREA = 1 << 8,
|
||||
}
|
||||
|
||||
foreign libroot {
|
||||
create_area :: proc(name: cstring, startAddress: ^rawptr, addressSpec: address_spec, size: c.size_t, lock: area_locking, protection: area_protection_flags) -> area_id ---
|
||||
clone_area :: proc(name: cstring, destAddress: ^rawptr, addressSpec: address_spec, protection: area_protection_flags, source: area_id) -> area_id ---
|
||||
find_area :: proc(name: cstring) -> area_id ---
|
||||
area_for :: proc(address: rawptr) -> area_id ---
|
||||
delete_area :: proc(id: area_id) -> status_t ---
|
||||
resize_area :: proc(id: area_id, newSize: c.size_t) -> status_t ---
|
||||
set_area_protection :: proc(id: area_id, newProtection: area_protection_flags) -> status_t ---
|
||||
_get_area_info :: proc(id: area_id, areaInfo: ^area_info, size: c.size_t) -> status_t ---
|
||||
_get_next_area_info :: proc(team: team_id, cookie: ^c.ssize_t, areaInfo: ^area_info, size: c.size_t) -> status_t ---
|
||||
}
|
||||
|
||||
// Ports
|
||||
|
||||
port_info :: struct {
|
||||
port: port_id,
|
||||
team: team_id,
|
||||
name: [OS_NAME_LENGTH]c.char,
|
||||
capacity: i32, // queue depth
|
||||
queue_count: i32, // # msgs waiting to be read
|
||||
total_count: i32, // total # msgs read so far
|
||||
}
|
||||
|
||||
port_flags :: enum u32 {
|
||||
USE_USER_MEMCPY = 0x80000000,
|
||||
// read the message, but don't remove it; kernel-only; memory must be locked
|
||||
PEEK_PORT_MESSAGE = 0x100,
|
||||
}
|
||||
|
||||
foreign libroot {
|
||||
create_port :: proc(capacity: i32, name: cstring) -> port_id ---
|
||||
find_port :: proc(name: cstring) -> port_id ---
|
||||
read_port :: proc(port: port_id, code: ^i32, buffer: rawptr, bufferSize: c.size_t) -> c.ssize_t ---
|
||||
read_port_etc :: proc(port: port_id, code: ^i32, buffer: rawptr, bufferSize: c.size_t, flags: port_flags, timeout: bigtime_t) -> c.ssize_t ---
|
||||
write_port :: proc(port: port_id, code: i32, buffer: rawptr, bufferSize: c.size_t) -> status_t ---
|
||||
write_port_etc :: proc(port: port_id, code: i32, buffer: rawptr, bufferSize: c.size_t, flags: port_flags, timeout: bigtime_t) -> status_t ---
|
||||
close_port :: proc(port: port_id) -> status_t ---
|
||||
delete_port :: proc(port: port_id) -> status_t ---
|
||||
port_buffer_size :: proc(port: port_id) -> c.ssize_t ---
|
||||
port_buffer_size_etc :: proc(port: port_id, flags: port_flags, timeout: bigtime_t) -> c.ssize_t ---
|
||||
port_count :: proc(port: port_id) -> c.ssize_t ---
|
||||
set_port_owner :: proc(port: port_id, team: team_id) -> status_t ---
|
||||
_get_port_info :: proc(port: port_id, portInfo: ^port_info, portInfoSize: c.size_t) -> status_t ---
|
||||
_get_next_port_info :: proc(team: team_id, cookie: ^i32, portInfo: ^port_info, portInfoSize: c.size_t) -> status_t ---
|
||||
}
|
||||
|
||||
// Semaphores
|
||||
|
||||
sem_info :: struct {
|
||||
sem: sem_id,
|
||||
team: team_id,
|
||||
name: [OS_NAME_LENGTH]c.char,
|
||||
count: i32,
|
||||
latest_holder: thread_id,
|
||||
}
|
||||
|
||||
semaphore_flags :: enum u32 {
|
||||
CAN_INTERRUPT = 0x01, // acquisition of the semaphore can be interrupted (system use only)
|
||||
CHECK_PERMISSION = 0x04, // ownership will be checked (system use only)
|
||||
KILL_CAN_INTERRUPT = 0x20, // acquisition of the semaphore can be interrupted by SIGKILL[THR], even if not CAN_INTERRUPT (system use only)
|
||||
|
||||
// release_sem_etc() only flags
|
||||
DO_NOT_RESCHEDULE = 0x02, // thread is not rescheduled
|
||||
RELEASE_ALL = 0x08, // all waiting threads will be woken up, count will be zeroed
|
||||
RELEASE_IF_WAITING_ONLY = 0x10, // release count only if there are any threads waiting
|
||||
}
|
||||
|
||||
foreign libroot {
|
||||
create_sem :: proc(count: i32, name: cstring) -> sem_id ---
|
||||
delete_sem :: proc(id: sem_id) -> status_t ---
|
||||
acquire_sem :: proc(id: sem_id) -> status_t ---
|
||||
acquire_sem_etc :: proc(id: sem_id, count: i32, flags: semaphore_flags, timeout: bigtime_t) -> status_t ---
|
||||
release_sem :: proc(id: sem_id) -> status_t ---
|
||||
release_sem_etc :: proc(id: sem_id, count: i32, flags: semaphore_flags) -> status_t ---
|
||||
switch_sem :: proc(semToBeReleased: sem_id) -> status_t ---
|
||||
switch_sem_etc :: proc(semToBeReleased: sem_id, id: sem_id, count: i32, flags: semaphore_flags, timeout: bigtime_t) -> status_t ---
|
||||
get_sem_count :: proc(id: sem_id, threadCount: ^i32) -> status_t ---
|
||||
set_sem_owner :: proc(id: sem_id, team: team_id) -> status_t ---
|
||||
_get_sem_info :: proc(id: sem_id, info: ^sem_info, infoSize: c.size_t) -> status_t ---
|
||||
_get_next_sem_info :: proc(team: team_id, cookie: ^i32, info: ^sem_info, infoSize: c.size_t) -> status_t ---
|
||||
}
|
||||
|
||||
// Teams
|
||||
|
||||
team_info :: struct {
|
||||
team: team_id,
|
||||
thread_count: i32,
|
||||
image_count: i32,
|
||||
area_count: i32,
|
||||
debugger_nub_thread: thread_id,
|
||||
debugger_nub_port: port_id,
|
||||
argc: i32,
|
||||
args: [64]c.char,
|
||||
uid: uid_t,
|
||||
gid: gid_t,
|
||||
|
||||
// Haiku R1 extensions
|
||||
real_uid: uid_t,
|
||||
real_gid: gid_t,
|
||||
group_id: pid_t,
|
||||
session_id: pid_t,
|
||||
parent: team_id,
|
||||
name: [OS_NAME_LENGTH]c.char,
|
||||
start_time: bigtime_t,
|
||||
}
|
||||
|
||||
CURRENT_TEAM :: 0
|
||||
SYSTEM_TEAM :: 1
|
||||
|
||||
team_usage_info :: struct {
|
||||
user_time: bigtime_t,
|
||||
kernel_time: bigtime_t,
|
||||
}
|
||||
|
||||
team_usage_who :: enum i32 {
|
||||
// compatible to sys/resource.h RUSAGE_SELF and RUSAGE_CHILDREN
|
||||
SELF = 0,
|
||||
CHILDREN = -1,
|
||||
}
|
||||
|
||||
foreign libroot {
|
||||
// see also: send_signal()
|
||||
kill_team :: proc(team: team_id) -> status_t ---
|
||||
_get_team_info :: proc(id: team_id, info: ^team_info, size: c.size_t) -> status_t ---
|
||||
_get_next_team_info :: proc(cookie: ^i32, info: ^team_info, size: c.size_t) -> status_t ---
|
||||
_get_team_usage_info :: proc(id: team_id, who: team_usage_who, info: ^team_usage_info, size: c.size_t) -> status_t ---
|
||||
}
|
||||
|
||||
// Threads
|
||||
|
||||
thread_state :: enum c.int {
|
||||
RUNNING = 1,
|
||||
READY,
|
||||
RECEIVING,
|
||||
ASLEEP,
|
||||
SUSPENDED,
|
||||
WAITING,
|
||||
}
|
||||
|
||||
thread_info :: struct {
|
||||
thread: thread_id,
|
||||
team: team_id,
|
||||
name: [OS_NAME_LENGTH]c.char,
|
||||
state: thread_state,
|
||||
priority: thread_priority,
|
||||
sem: sem_id,
|
||||
user_time: bigtime_t,
|
||||
kernel_time: bigtime_t,
|
||||
stack_base: rawptr,
|
||||
stack_end: rawptr,
|
||||
}
|
||||
|
||||
thread_priority :: enum i32 {
|
||||
IDLE_PRIORITY = 0,
|
||||
LOWEST_ACTIVE_PRIORITY = 1,
|
||||
LOW_PRIORITY = 5,
|
||||
NORMAL_PRIORITY = 10,
|
||||
DISPLAY_PRIORITY = 15,
|
||||
URGENT_DISPLAY_PRIORITY = 20,
|
||||
REAL_TIME_DISPLAY_PRIORITY = 100,
|
||||
URGENT_PRIORITY = 110,
|
||||
REAL_TIME_PRIORITY = 120,
|
||||
}
|
||||
|
||||
FIRST_REAL_TIME_PRIORITY :: thread_priority.REAL_TIME_PRIORITY
|
||||
|
||||
// time base for snooze_*(), compatible with the clockid_t constants defined in <time.h>
|
||||
SYSTEM_TIMEBASE :: 0
|
||||
|
||||
thread_func :: #type proc "c" (rawptr) -> status_t
|
||||
|
||||
foreign libroot {
|
||||
spawn_thread :: proc(thread_func, name: cstring, priority: thread_priority, data: rawptr) -> thread_id ---
|
||||
kill_thread :: proc(thread: thread_id) -> status_t ---
|
||||
resume_thread :: proc(thread: thread_id) -> status_t ---
|
||||
suspend_thread :: proc(thread: thread_id) -> status_t ---
|
||||
rename_thread :: proc(thread: thread_id, newName: cstring) -> status_t ---
|
||||
set_thread_priority :: proc(thread: thread_id, newPriority: thread_priority) -> status_t ---
|
||||
exit_thread :: proc(status: status_t) ---
|
||||
wait_for_thread :: proc(thread: thread_id, returnValue: ^status_t) -> status_t ---
|
||||
// FIXME: Find and define those flags.
|
||||
wait_for_thread_etc :: proc(id: thread_id, flags: u32, timeout: bigtime_t, _returnCode: ^status_t) -> status_t ---
|
||||
on_exit_thread :: proc(callback: proc "c" (rawptr), data: rawptr) -> status_t ---
|
||||
find_thread :: proc(name: cstring) -> thread_id ---
|
||||
send_data :: proc(thread: thread_id, code: i32, buffer: rawptr, bufferSize: c.size_t) -> status_t ---
|
||||
receive_data :: proc(sender: ^thread_id, buffer: rawptr, bufferSize: c.size_t) -> i32 ---
|
||||
has_data :: proc(thread: thread_id) -> bool ---
|
||||
snooze :: proc(amount: bigtime_t) -> status_t ---
|
||||
// FIXME: Find and define those flags.
|
||||
snooze_etc :: proc(amount: bigtime_t, timeBase: c.int, flags: u32) -> status_t ---
|
||||
snooze_until :: proc(time: bigtime_t, timeBase: c.int) -> status_t ---
|
||||
_get_thread_info :: proc(id: thread_id, info: ^thread_info, size: c.size_t) -> status_t ---
|
||||
_get_next_thread_info :: proc(team: team_id, cookie: ^i32, info: ^thread_info, size: c.size_t) -> status_t ---
|
||||
// bridge to the pthread API
|
||||
get_pthread_thread_id :: proc(thread: pthread_t) -> thread_id ---
|
||||
}
|
||||
|
||||
// Time
|
||||
|
||||
foreign libroot {
|
||||
real_time_clock :: proc() -> c.ulong ---
|
||||
set_real_time_clock :: proc(secsSinceJan1st1970: c.ulong) ---
|
||||
real_time_clock_usecs :: proc() -> bigtime_t ---
|
||||
// time since booting in microseconds
|
||||
system_time :: proc() -> bigtime_t ---
|
||||
// time since booting in nanoseconds
|
||||
system_time_nsecs :: proc() -> nanotime_t ---
|
||||
}
|
||||
|
||||
// Alarm
|
||||
|
||||
alarm_mode :: enum u32 {
|
||||
ONE_SHOT_ABSOLUTE_ALARM = 1,
|
||||
ONE_SHOT_RELATIVE_ALARM,
|
||||
PERIODIC_ALARM, // "when" specifies the period
|
||||
}
|
||||
|
||||
foreign libroot {
|
||||
set_alarm :: proc(_when: bigtime_t, mode: alarm_mode) -> bigtime_t ---
|
||||
}
|
||||
|
||||
// Debugger
|
||||
|
||||
foreign libroot {
|
||||
debugger :: proc(message: cstring) ---
|
||||
/*
|
||||
calling this function with a non-zero value will cause your thread
|
||||
to receive signals for any exceptional conditions that occur (i.e.
|
||||
you'll get SIGSEGV for data access exceptions, SIGFPE for floating
|
||||
point errors, SIGILL for illegal instructions, etc).
|
||||
|
||||
to re-enable the default debugger pass a zero.
|
||||
*/
|
||||
disable_debugger :: proc(state: c.int) -> c.int ---
|
||||
}
|
||||
|
||||
// System information
|
||||
|
||||
cpu_info :: struct {
|
||||
active_time: bigtime_t,
|
||||
enabled: bool,
|
||||
current_frequency: u64,
|
||||
}
|
||||
|
||||
system_info :: struct {
|
||||
boot_time: bigtime_t, // time of boot (usecs since 1/1/1970)
|
||||
|
||||
cpu_count: u32, // number of cpus
|
||||
|
||||
max_pages: u64, // total # of accessible pages
|
||||
used_pages: u64, // # of accessible pages in use
|
||||
cached_pages: u64,
|
||||
block_cache_pages: u64,
|
||||
ignored_pages: u64, // # of ignored/inaccessible pages
|
||||
|
||||
needed_memory: u64,
|
||||
free_memory: u64,
|
||||
|
||||
max_swap_pages: u64,
|
||||
free_swap_pages: u64,
|
||||
|
||||
page_faults: u32, // # of page faults
|
||||
|
||||
max_sems: u32,
|
||||
used_sems: u32,
|
||||
|
||||
max_ports: u32,
|
||||
used_ports: u32,
|
||||
|
||||
max_threads: u32,
|
||||
used_threads: u32,
|
||||
|
||||
max_teams: u32,
|
||||
used_teams: u32,
|
||||
|
||||
kernel_name: [FILE_NAME_LENGTH]c.char,
|
||||
kernel_build_date: [OS_NAME_LENGTH]c.char,
|
||||
kernel_build_time: [OS_NAME_LENGTH]c.char,
|
||||
|
||||
kernel_version: i64,
|
||||
abi: u32, // the system API
|
||||
}
|
||||
|
||||
topology_level_type :: enum c.int {
|
||||
UNKNOWN,
|
||||
ROOT,
|
||||
SMT,
|
||||
CORE,
|
||||
PACKAGE,
|
||||
}
|
||||
|
||||
cpu_platform :: enum c.int {
|
||||
UNKNOWN,
|
||||
x86,
|
||||
x86_64,
|
||||
PPC,
|
||||
PPC_64,
|
||||
M68K,
|
||||
ARM,
|
||||
ARM_64,
|
||||
ALPHA,
|
||||
MIPS,
|
||||
SH,
|
||||
SPARC,
|
||||
RISC_V,
|
||||
}
|
||||
|
||||
cpu_vendor :: enum c.int {
|
||||
UNKNOWN,
|
||||
AMD,
|
||||
CYRIX,
|
||||
IDT,
|
||||
INTEL,
|
||||
NATIONAL_SEMICONDUCTOR,
|
||||
RISE,
|
||||
TRANSMETA,
|
||||
VIA,
|
||||
IBM,
|
||||
MOTOROLA,
|
||||
NEC,
|
||||
HYGON,
|
||||
SUN,
|
||||
FUJITSU,
|
||||
}
|
||||
|
||||
cpu_topology_node_info :: struct {
|
||||
id: u32,
|
||||
type: topology_level_type,
|
||||
level: u32,
|
||||
|
||||
data: struct #raw_union {
|
||||
_root: struct {
|
||||
platform: cpu_platform,
|
||||
},
|
||||
_package: struct {
|
||||
vendor: cpu_vendor,
|
||||
cache_line_size: u32
|
||||
},
|
||||
_core: struct {
|
||||
model: u32,
|
||||
default_frequency: u64,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// FIXME: Add cpuid_info when bit fields are ready.
|
||||
|
||||
foreign libroot {
|
||||
get_system_info :: proc(info: ^system_info) -> status_t ---
|
||||
_get_cpu_info_etc :: proc(firstCPU: u32, cpuCount: u32, info: ^cpu_info, size: c.size_t) -> status_t ---
|
||||
get_cpu_topology_info :: proc(topologyInfos: [^]cpu_topology_node_info, topologyInfoCount: ^u32) -> status_t ---
|
||||
|
||||
is_computer_on :: proc() -> i32 ---
|
||||
is_computer_on_fire :: proc() -> f64 ---
|
||||
}
|
||||
|
||||
// Signal.h
|
||||
|
||||
SIG_BLOCK :: 1
|
||||
SIG_UNBLOCK :: 2
|
||||
SIG_SETMASK :: 3
|
||||
|
||||
/*
|
||||
* The list of all defined signals:
|
||||
*
|
||||
* The numbering of signals for Haiku attempts to maintain
|
||||
* some consistency with UN*X conventions so that things
|
||||
* like "kill -9" do what you expect.
|
||||
*/
|
||||
|
||||
SIGHUP :: 1 // hangup -- tty is gone!
|
||||
SIGINT :: 2 // interrupt
|
||||
SIGQUIT :: 3 // `quit' special character typed in tty
|
||||
SIGILL :: 4 // illegal instruction
|
||||
SIGCHLD :: 5 // child process exited
|
||||
SIGABRT :: 6 // abort() called, dont' catch
|
||||
SIGPIPE :: 7 // write to a pipe w/no readers
|
||||
SIGFPE :: 8 // floating point exception
|
||||
SIGKILL :: 9 // kill a team (not catchable)
|
||||
SIGSTOP :: 10 // suspend a thread (not catchable)
|
||||
SIGSEGV :: 11 // segmentation violation (read: invalid pointer)
|
||||
SIGCONT :: 12 // continue execution if suspended
|
||||
SIGTSTP :: 13 // `stop' special character typed in tty
|
||||
SIGALRM :: 14 // an alarm has gone off (see alarm())
|
||||
SIGTERM :: 15 // termination requested
|
||||
SIGTTIN :: 16 // read of tty from bg process
|
||||
SIGTTOU :: 17 // write to tty from bg process
|
||||
SIGUSR1 :: 18 // app defined signal 1
|
||||
SIGUSR2 :: 19 // app defined signal 2
|
||||
SIGWINCH :: 20 // tty window size changed
|
||||
SIGKILLTHR :: 21 // be specific: kill just the thread, not team
|
||||
SIGTRAP :: 22 // Trace/breakpoint trap
|
||||
SIGPOLL :: 23 // Pollable event
|
||||
SIGPROF :: 24 // Profiling timer expired
|
||||
SIGSYS :: 25 // Bad system call
|
||||
SIGURG :: 26 // High bandwidth data is available at socket
|
||||
SIGVTALRM :: 27 // Virtual timer expired
|
||||
SIGXCPU :: 28 // CPU time limit exceeded
|
||||
SIGXFSZ :: 29 // File size limit exceeded
|
||||
SIGBUS :: 30 // access to undefined portion of a memory object
|
||||
|
||||
sigval :: struct #raw_union {
|
||||
sival_int: c.int,
|
||||
sival_ptr: rawptr,
|
||||
}
|
||||
|
||||
siginfo_t :: struct {
|
||||
si_signo: c.int, // signal number
|
||||
si_code: c.int, // signal code
|
||||
si_errno: c.int, // if non zero, an error number associated with this signal
|
||||
|
||||
si_pid: pid_t, // sending process ID
|
||||
si_uid: uid_t, // real user ID of sending process
|
||||
si_addr: rawptr, // address of faulting instruction
|
||||
si_status: c.int, // exit value or signal
|
||||
si_band: c.long, // band event for SIGPOLL
|
||||
si_value: sigval, // signal value
|
||||
}
|
||||
|
||||
foreign libroot {
|
||||
// signal set (sigset_t) manipulation
|
||||
sigemptyset :: proc(set: ^sigset_t) -> c.int ---
|
||||
sigfillset :: proc(set: ^sigset_t) -> c.int ---
|
||||
sigaddset :: proc(set: ^sigset_t, _signal: c.int) -> c.int ---
|
||||
sigdelset :: proc(set: ^sigset_t, _signal: c.int) -> c.int ---
|
||||
sigismember :: proc(set: ^sigset_t, _signal: c.int) -> c.int ---
|
||||
// querying and waiting for signals
|
||||
sigpending :: proc(set: ^sigset_t) -> c.int ---
|
||||
sigsuspend :: proc(mask: ^sigset_t) -> c.int ---
|
||||
sigpause :: proc(_signal: c.int) -> c.int ---
|
||||
sigwait :: proc(set: ^sigset_t, _signal: ^c.int) -> c.int ---
|
||||
sigwaitinfo :: proc(set: ^sigset_t, info: ^siginfo_t) -> c.int ---
|
||||
sigtimedwait :: proc(set: ^sigset_t, info: ^siginfo_t, timeout: ^unix.timespec) -> c.int ---
|
||||
|
||||
send_signal :: proc(threadID: thread_id, signal: c.uint) -> c.int ---
|
||||
set_signal_stack :: proc(base: rawptr, size: c.size_t) ---
|
||||
}
|
||||
54
core/sys/haiku/types.odin
Normal file
54
core/sys/haiku/types.odin
Normal file
@@ -0,0 +1,54 @@
|
||||
//+build haiku
|
||||
package sys_haiku
|
||||
|
||||
import "core:c"
|
||||
|
||||
status_t :: i32
|
||||
bigtime_t :: i64
|
||||
nanotime_t :: i64
|
||||
type_code :: u32
|
||||
perform_code :: u32
|
||||
|
||||
phys_addr_t :: uintptr
|
||||
phys_size_t :: phys_addr_t
|
||||
generic_addr_t :: uintptr
|
||||
generic_size_t :: generic_addr_t
|
||||
|
||||
area_id :: i32
|
||||
port_id :: i32
|
||||
sem_id :: i32
|
||||
team_id :: i32
|
||||
thread_id :: i32
|
||||
|
||||
blkcnt_t :: i64
|
||||
blksize_t :: i32
|
||||
fsblkcnt_t :: i64
|
||||
fsfilcnt_t :: i64
|
||||
off_t :: i64
|
||||
ino_t :: i64
|
||||
cnt_t :: i32
|
||||
dev_t :: i32
|
||||
pid_t :: i32
|
||||
id_t :: i32
|
||||
|
||||
uid_t :: u32
|
||||
gid_t :: u32
|
||||
mode_t :: u32
|
||||
umode_t :: u32
|
||||
nlink_t :: i32
|
||||
|
||||
caddr_t :: ^c.char
|
||||
|
||||
addr_t :: phys_addr_t
|
||||
key_t :: i32
|
||||
|
||||
clockid_t :: i32
|
||||
|
||||
time_t :: i64 when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 else i32
|
||||
|
||||
sig_atomic_t :: c.int
|
||||
sigset_t :: u64
|
||||
|
||||
image_id :: i32
|
||||
|
||||
pthread_t :: rawptr
|
||||
71
core/sys/unix/pthread_haiku.odin
Normal file
71
core/sys/unix/pthread_haiku.odin
Normal file
@@ -0,0 +1,71 @@
|
||||
package unix
|
||||
|
||||
import "core:c"
|
||||
|
||||
pthread_t :: distinct rawptr
|
||||
pthread_attr_t :: distinct rawptr
|
||||
pthread_mutex_t :: distinct rawptr
|
||||
pthread_mutexattr_t :: distinct rawptr
|
||||
pthread_cond_t :: distinct rawptr
|
||||
pthread_condattr_t :: distinct rawptr
|
||||
pthread_rwlock_t :: distinct rawptr
|
||||
pthread_rwlockattr_t :: distinct rawptr
|
||||
pthread_barrier_t :: distinct rawptr
|
||||
pthread_barrierattr_t :: distinct rawptr
|
||||
pthread_spinlock_t :: distinct rawptr
|
||||
|
||||
pthread_key_t :: distinct c.int
|
||||
pthread_once_t :: struct {
|
||||
state: c.int,
|
||||
mutex: pthread_mutex_t,
|
||||
}
|
||||
|
||||
PTHREAD_MUTEX_DEFAULT :: 0
|
||||
PTHREAD_MUTEX_NORMAL :: 1
|
||||
PTHREAD_MUTEX_ERRORCHECK :: 2
|
||||
PTHREAD_MUTEX_RECURSIVE :: 3
|
||||
|
||||
PTHREAD_DETACHED :: 0x1
|
||||
PTHREAD_SCOPE_SYSTEM :: 0x2
|
||||
PTHREAD_INHERIT_SCHED :: 0x4
|
||||
PTHREAD_NOFLOAT :: 0x8
|
||||
|
||||
PTHREAD_CREATE_DETACHED :: PTHREAD_DETACHED
|
||||
PTHREAD_CREATE_JOINABLE :: 0
|
||||
PTHREAD_SCOPE_PROCESS :: 0
|
||||
PTHREAD_EXPLICIT_SCHED :: 0
|
||||
|
||||
SCHED_FIFO :: 1
|
||||
SCHED_RR :: 2
|
||||
SCHED_SPORADIC :: 3
|
||||
SCHED_OTHER :: 4
|
||||
|
||||
sched_param :: struct {
|
||||
sched_priority: c.int,
|
||||
}
|
||||
|
||||
sem_t :: distinct rawptr
|
||||
|
||||
PTHREAD_CANCEL_ENABLE :: 0
|
||||
PTHREAD_CANCEL_DISABLE :: 1
|
||||
PTHREAD_CANCEL_DEFERRED :: 0
|
||||
PTHREAD_CANCEL_ASYNCHRONOUS :: 2
|
||||
|
||||
foreign import libc "system:c"
|
||||
|
||||
@(default_calling_convention="c")
|
||||
foreign libc {
|
||||
sem_open :: proc(name: cstring, flags: c.int) -> ^sem_t ---
|
||||
|
||||
sem_init :: proc(sem: ^sem_t, pshared: c.int, initial_value: c.uint) -> c.int ---
|
||||
sem_destroy :: proc(sem: ^sem_t) -> c.int ---
|
||||
sem_post :: proc(sem: ^sem_t) -> c.int ---
|
||||
sem_wait :: proc(sem: ^sem_t) -> c.int ---
|
||||
sem_trywait :: proc(sem: ^sem_t) -> c.int ---
|
||||
|
||||
pthread_yield :: proc() ---
|
||||
|
||||
pthread_setcancelstate :: proc (state: c.int, old_state: ^c.int) -> c.int ---
|
||||
pthread_setcanceltype :: proc (type: c.int, old_type: ^c.int) -> c.int ---
|
||||
pthread_cancel :: proc (thread: pthread_t) -> c.int ---
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
//+build linux, darwin, freebsd, openbsd
|
||||
//+build linux, darwin, freebsd, openbsd, haiku
|
||||
package unix
|
||||
|
||||
foreign import "system:pthread"
|
||||
@@ -16,6 +16,8 @@ foreign pthread {
|
||||
// retval is a pointer to a location to put the return value of the thread proc.
|
||||
pthread_join :: proc(t: pthread_t, retval: ^rawptr) -> c.int ---
|
||||
|
||||
pthread_kill :: proc(t: pthread_t, sig: c.int) -> c.int ---
|
||||
|
||||
pthread_self :: proc() -> pthread_t ---
|
||||
|
||||
pthread_equal :: proc(a, b: pthread_t) -> b32 ---
|
||||
@@ -31,15 +33,9 @@ foreign pthread {
|
||||
pthread_attr_getschedparam :: proc(attrs: ^pthread_attr_t, param: ^sched_param) -> c.int ---
|
||||
pthread_attr_setschedparam :: proc(attrs: ^pthread_attr_t, param: ^sched_param) -> c.int ---
|
||||
|
||||
pthread_attr_getschedpolicy :: proc(t: ^pthread_attr_t, policy: ^c.int) -> c.int ---
|
||||
pthread_attr_setschedpolicy :: proc(t: ^pthread_attr_t, policy: c.int) -> c.int ---
|
||||
|
||||
// states: PTHREAD_CREATE_DETACHED, PTHREAD_CREATE_JOINABLE
|
||||
pthread_attr_setdetachstate :: proc(attrs: ^pthread_attr_t, detach_state: c.int) -> c.int ---
|
||||
|
||||
// scheds: PTHREAD_INHERIT_SCHED, PTHREAD_EXPLICIT_SCHED
|
||||
pthread_attr_setinheritsched :: proc(attrs: ^pthread_attr_t, sched: c.int) -> c.int ---
|
||||
|
||||
|
||||
// NOTE(tetra, 2019-11-06): WARNING: Different systems have different alignment requirements.
|
||||
// For maximum usefulness, use the OS's page size.
|
||||
// ALSO VERY MAJOR WARNING: `stack_ptr` must be the LAST byte of the stack on systems
|
||||
@@ -52,8 +48,20 @@ foreign pthread {
|
||||
pthread_attr_setstack :: proc(attrs: ^pthread_attr_t, stack_ptr: rawptr, stack_size: u64) -> c.int ---
|
||||
pthread_attr_getstack :: proc(attrs: ^pthread_attr_t, stack_ptr: ^rawptr, stack_size: ^u64) -> c.int ---
|
||||
|
||||
sched_yield :: proc() -> c.int ---
|
||||
pthread_sigmask :: proc(how: c.int, set: rawptr, oldset: rawptr) -> c.int ---
|
||||
|
||||
sched_yield :: proc() -> c.int ---
|
||||
}
|
||||
|
||||
// NOTE: Unimplemented in Haiku.
|
||||
when ODIN_OS != .Haiku {
|
||||
foreign pthread {
|
||||
// scheds: PTHREAD_INHERIT_SCHED, PTHREAD_EXPLICIT_SCHED
|
||||
pthread_attr_setinheritsched :: proc(attrs: ^pthread_attr_t, sched: c.int) -> c.int ---
|
||||
|
||||
pthread_attr_getschedpolicy :: proc(t: ^pthread_attr_t, policy: ^c.int) -> c.int ---
|
||||
pthread_attr_setschedpolicy :: proc(t: ^pthread_attr_t, policy: c.int) -> c.int ---
|
||||
}
|
||||
}
|
||||
|
||||
@(default_calling_convention="c")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//+build linux, darwin, freebsd, openbsd
|
||||
//+build linux, darwin, freebsd, openbsd, haiku
|
||||
package unix
|
||||
|
||||
when ODIN_OS == .Darwin {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// +build windows
|
||||
package sys_windows
|
||||
|
||||
import "core:math/fixed"
|
||||
|
||||
foreign import gdi32 "system:Gdi32.lib"
|
||||
|
||||
@(default_calling_convention="system")
|
||||
@@ -11,6 +13,7 @@ foreign gdi32 {
|
||||
SetBkColor :: proc(hdc: HDC, color: COLORREF) -> COLORREF ---
|
||||
|
||||
CreateCompatibleDC :: proc(hdc: HDC) -> HDC ---
|
||||
DeleteDC :: proc(hdc: HDC) -> BOOL ---
|
||||
|
||||
CreateDIBPatternBrush :: proc(h: HGLOBAL, iUsage: UINT) -> HBRUSH ---
|
||||
|
||||
@@ -93,3 +96,45 @@ foreign gdi32 {
|
||||
RGB :: #force_inline proc "contextless" (r, g, b: u8) -> COLORREF {
|
||||
return transmute(COLORREF)[4]u8{r, g, b, 0}
|
||||
}
|
||||
|
||||
FXPT2DOT30 :: distinct fixed.Fixed(i32, 30)
|
||||
|
||||
CIEXYZ :: struct {
|
||||
ciexyzX: FXPT2DOT30,
|
||||
ciexyzY: FXPT2DOT30,
|
||||
ciexyzZ: FXPT2DOT30,
|
||||
}
|
||||
|
||||
CIEXYZTRIPLE :: struct {
|
||||
ciexyzRed: CIEXYZ,
|
||||
ciexyzGreen: CIEXYZ,
|
||||
ciexyzBlue: CIEXYZ,
|
||||
}
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapv5header
|
||||
BITMAPV5HEADER :: struct {
|
||||
bV5Size: DWORD,
|
||||
bV5Width: LONG,
|
||||
bV5Height: LONG,
|
||||
bV5Planes: WORD,
|
||||
bV5BitCount: WORD,
|
||||
bV5Compression: DWORD,
|
||||
bV5SizeImage: DWORD,
|
||||
bV5XPelsPerMeter: LONG,
|
||||
bV5YPelsPerMeter: LONG,
|
||||
bV5ClrUsed: DWORD,
|
||||
bV5ClrImportant: DWORD,
|
||||
bV5RedMask: DWORD,
|
||||
bV5GreenMask: DWORD,
|
||||
bV5BlueMask: DWORD,
|
||||
bV5AlphaMask: DWORD,
|
||||
bV5CSType: DWORD,
|
||||
bV5Endpoints: CIEXYZTRIPLE,
|
||||
bV5GammaRed: DWORD,
|
||||
bV5GammaGreen: DWORD,
|
||||
bV5GammaBlue: DWORD,
|
||||
bV5Intent: DWORD,
|
||||
bV5ProfileData: DWORD,
|
||||
bV5ProfileSize: DWORD,
|
||||
bV5Reserved: DWORD,
|
||||
}
|
||||
|
||||
@@ -1200,3 +1200,22 @@ SYSTEM_LOGICAL_PROCESSOR_INFORMATION :: struct {
|
||||
Relationship: LOGICAL_PROCESSOR_RELATIONSHIP,
|
||||
DummyUnion: DUMMYUNIONNAME_u,
|
||||
}
|
||||
|
||||
/* Global Memory Flags */
|
||||
GMEM_FIXED :: 0x0000
|
||||
GMEM_MOVEABLE :: 0x0002
|
||||
GMEM_NOCOMPACT :: 0x0010
|
||||
GMEM_NODISCARD :: 0x0020
|
||||
GMEM_ZEROINIT :: 0x0040
|
||||
GMEM_MODIFY :: 0x0080
|
||||
GMEM_DISCARDABLE :: 0x0100
|
||||
GMEM_NOT_BANKED :: 0x1000
|
||||
GMEM_SHARE :: 0x2000
|
||||
GMEM_DDESHARE :: 0x2000
|
||||
GMEM_NOTIFY :: 0x4000
|
||||
GMEM_LOWER :: GMEM_NOT_BANKED
|
||||
GMEM_VALID_FLAGS :: 0x7F72
|
||||
GMEM_INVALID_HANDLE :: 0x8000
|
||||
|
||||
GHND :: (GMEM_MOVEABLE | GMEM_ZEROINIT)
|
||||
GPTR :: (GMEM_FIXED | GMEM_ZEROINIT)
|
||||
|
||||
@@ -25,6 +25,7 @@ foreign shell32 {
|
||||
SHAppBarMessage :: proc(dwMessage: DWORD, pData: PAPPBARDATA) -> UINT_PTR ---
|
||||
|
||||
Shell_NotifyIconW :: proc(dwMessage: DWORD, lpData: ^NOTIFYICONDATAW) -> BOOL ---
|
||||
SHChangeNotify :: proc(wEventId: LONG, uFlags: UINT, dwItem1: LPCVOID, dwItem2: LPCVOID) ---
|
||||
|
||||
SHGetKnownFolderIDList :: proc(rfid: REFKNOWNFOLDERID, dwFlags: /* KNOWN_FOLDER_FLAG */ DWORD, hToken: HANDLE, ppidl: rawptr) -> HRESULT ---
|
||||
SHSetKnownFolderPath :: proc(rfid: REFKNOWNFOLDERID, dwFlags: /* KNOWN_FOLDER_FLAG */ DWORD, hToken: HANDLE, pszPath: PCWSTR ) -> HRESULT ---
|
||||
@@ -91,3 +92,53 @@ KNOWN_FOLDER_FLAG :: enum u32 {
|
||||
SIMPLE_IDLIST = 0x00000100,
|
||||
ALIAS_ONLY = 0x80000000,
|
||||
}
|
||||
|
||||
SHCNRF_InterruptLevel :: 0x0001
|
||||
SHCNRF_ShellLevel :: 0x0002
|
||||
SHCNRF_RecursiveInterrupt :: 0x1000
|
||||
SHCNRF_NewDelivery :: 0x8000
|
||||
|
||||
SHCNE_RENAMEITEM :: 0x00000001
|
||||
SHCNE_CREATE :: 0x00000002
|
||||
SHCNE_DELETE :: 0x00000004
|
||||
SHCNE_MKDIR :: 0x00000008
|
||||
SHCNE_RMDIR :: 0x00000010
|
||||
SHCNE_MEDIAINSERTED :: 0x00000020
|
||||
SHCNE_MEDIAREMOVED :: 0x00000040
|
||||
SHCNE_DRIVEREMOVED :: 0x00000080
|
||||
SHCNE_DRIVEADD :: 0x00000100
|
||||
SHCNE_NETSHARE :: 0x00000200
|
||||
SHCNE_NETUNSHARE :: 0x00000400
|
||||
SHCNE_ATTRIBUTES :: 0x00000800
|
||||
SHCNE_UPDATEDIR :: 0x00001000
|
||||
SHCNE_UPDATEITEM :: 0x00002000
|
||||
SHCNE_SERVERDISCONNECT :: 0x00004000
|
||||
SHCNE_UPDATEIMAGE :: 0x00008000
|
||||
SHCNE_DRIVEADDGUI :: 0x00010000
|
||||
SHCNE_RENAMEFOLDER :: 0x00020000
|
||||
SHCNE_FREESPACE :: 0x00040000
|
||||
|
||||
SHCNE_EXTENDED_EVENT :: 0x04000000
|
||||
|
||||
SHCNE_ASSOCCHANGED :: 0x08000000
|
||||
|
||||
SHCNE_DISKEVENTS :: 0x0002381F
|
||||
SHCNE_GLOBALEVENTS :: 0x0C0581E0
|
||||
SHCNE_ALLEVENTS :: 0x7FFFFFFF
|
||||
SHCNE_INTERRUPT :: 0x80000000
|
||||
|
||||
SHCNEE_ORDERCHANGED :: 2
|
||||
SHCNEE_MSI_CHANGE :: 4
|
||||
SHCNEE_MSI_UNINSTALL :: 5
|
||||
|
||||
SHCNF_IDLIST :: 0x0000
|
||||
SHCNF_PATHA :: 0x0001
|
||||
SHCNF_PRINTERA :: 0x0002
|
||||
SHCNF_DWORD :: 0x0003
|
||||
SHCNF_PATHW :: 0x0005
|
||||
SHCNF_PRINTERW :: 0x0006
|
||||
SHCNF_TYPE :: 0x00FF
|
||||
SHCNF_FLUSH :: 0x1000
|
||||
SHCNF_FLUSHNOWAIT :: 0x3000
|
||||
|
||||
SHCNF_NOTIFYRECURSIVE :: 0x10000
|
||||
|
||||
@@ -47,6 +47,7 @@ foreign user32 {
|
||||
UpdateWindow :: proc(hWnd: HWND) -> BOOL ---
|
||||
SetActiveWindow :: proc(hWnd: HWND) -> HWND ---
|
||||
GetActiveWindow :: proc() -> HWND ---
|
||||
RedrawWindow :: proc(hwnd: HWND, lprcUpdate: LPRECT, hrgnUpdate: HRGN, flags: RedrawWindowFlags) -> BOOL ---
|
||||
|
||||
GetMessageW :: proc(lpMsg: ^MSG, hWnd: HWND, wMsgFilterMin: UINT, wMsgFilterMax: UINT) -> BOOL ---
|
||||
|
||||
@@ -85,6 +86,14 @@ foreign user32 {
|
||||
LoadCursorW :: proc(hInstance: HINSTANCE, lpCursorName: LPCWSTR) -> HCURSOR ---
|
||||
LoadImageW :: proc(hInst: HINSTANCE, name: LPCWSTR, type: UINT, cx: c_int, cy: c_int, fuLoad: UINT) -> HANDLE ---
|
||||
|
||||
CreateIcon :: proc(hInstance: HINSTANCE, nWidth: c_int, nHeight: c_int, cPlanes: BYTE, cBitsPixel: BYTE, lpbANDbits: PBYTE, lpbXORbits: PBYTE) -> HICON ---
|
||||
CreateIconFromResource :: proc(presbits: PBYTE, dwResSize: DWORD, fIcon: BOOL, dwVer: DWORD) -> HICON ---
|
||||
DestroyIcon :: proc(hIcon: HICON) -> BOOL ---
|
||||
DrawIcon :: proc(hDC: HDC, X: c_int, Y: c_int, hIcon: HICON) -> BOOL ---
|
||||
|
||||
CreateCursor :: proc(hInst: HINSTANCE, xHotSpot: c_int, yHotSpot: c_int, nWidth: c_int, nHeight: c_int, pvANDPlane: PVOID, pvXORPlane: PVOID) -> HCURSOR ---
|
||||
DestroyCursor :: proc(hCursor: HCURSOR) -> BOOL ---
|
||||
|
||||
GetWindowRect :: proc(hWnd: HWND, lpRect: LPRECT) -> BOOL ---
|
||||
GetClientRect :: proc(hWnd: HWND, lpRect: LPRECT) -> BOOL ---
|
||||
ClientToScreen :: proc(hWnd: HWND, lpPoint: LPPOINT) -> BOOL ---
|
||||
@@ -134,7 +143,7 @@ foreign user32 {
|
||||
|
||||
GetKeyState :: proc(nVirtKey: c_int) -> SHORT ---
|
||||
GetAsyncKeyState :: proc(vKey: c_int) -> SHORT ---
|
||||
|
||||
|
||||
GetKeyboardState :: proc(lpKeyState: PBYTE) -> BOOL ---
|
||||
|
||||
MapVirtualKeyW :: proc(uCode: UINT, uMapType: UINT) -> UINT ---
|
||||
@@ -166,7 +175,7 @@ foreign user32 {
|
||||
MonitorFromRect :: proc(lprc: LPRECT, dwFlags: Monitor_From_Flags) -> HMONITOR ---
|
||||
MonitorFromWindow :: proc(hwnd: HWND, dwFlags: Monitor_From_Flags) -> HMONITOR ---
|
||||
EnumDisplayMonitors :: proc(hdc: HDC, lprcClip: LPRECT, lpfnEnum: Monitor_Enum_Proc, dwData: LPARAM) -> BOOL ---
|
||||
|
||||
|
||||
EnumWindows :: proc(lpEnumFunc: Window_Enum_Proc, lParam: LPARAM) -> BOOL ---
|
||||
|
||||
SetThreadDpiAwarenessContext :: proc(dpiContext: DPI_AWARENESS_CONTEXT) -> DPI_AWARENESS_CONTEXT ---
|
||||
@@ -239,6 +248,9 @@ foreign user32 {
|
||||
|
||||
GetSystemMenu :: proc(hWnd: HWND, bRevert: BOOL) -> HMENU ---
|
||||
EnableMenuItem :: proc(hMenu: HMENU, uIDEnableItem: UINT, uEnable: UINT) -> BOOL ---
|
||||
|
||||
DrawTextW :: proc(hdc: HDC, lpchText: LPCWSTR, cchText: INT, lprc: LPRECT, format: DrawTextFormat) -> INT ---
|
||||
DrawTextExW :: proc(hdc: HDC, lpchText: LPCWSTR, cchText: INT, lprc: LPRECT, format: DrawTextFormat, lpdtp: PDRAWTEXTPARAMS) -> INT ---
|
||||
}
|
||||
|
||||
CreateWindowW :: #force_inline proc "system" (
|
||||
@@ -471,6 +483,19 @@ RI_MOUSE_BUTTON_5_UP :: 0x0200
|
||||
RI_MOUSE_WHEEL :: 0x0400
|
||||
RI_MOUSE_HWHEEL :: 0x0800
|
||||
|
||||
HID_USAGE_PAGE_GENERIC :: 0x01
|
||||
HID_USAGE_PAGE_GAME :: 0x05
|
||||
HID_USAGE_PAGE_LED :: 0x08
|
||||
HID_USAGE_PAGE_BUTTON :: 0x09
|
||||
|
||||
HID_USAGE_GENERIC_POINTER :: 0x01
|
||||
HID_USAGE_GENERIC_MOUSE :: 0x02
|
||||
HID_USAGE_GENERIC_JOYSTICK :: 0x04
|
||||
HID_USAGE_GENERIC_GAMEPAD :: 0x05
|
||||
HID_USAGE_GENERIC_KEYBOARD :: 0x06
|
||||
HID_USAGE_GENERIC_KEYPAD :: 0x07
|
||||
HID_USAGE_GENERIC_MULTI_AXIS_CONTROLLER :: 0x08
|
||||
|
||||
WINDOWPLACEMENT :: struct {
|
||||
length: UINT,
|
||||
flags: UINT,
|
||||
@@ -493,3 +518,54 @@ WINDOWINFO :: struct {
|
||||
wCreatorVersion: WORD,
|
||||
}
|
||||
PWINDOWINFO :: ^WINDOWINFO
|
||||
|
||||
DRAWTEXTPARAMS :: struct {
|
||||
cbSize : UINT ,
|
||||
iTabLength: int ,
|
||||
iLeftMargin: int ,
|
||||
iRightMargin: int ,
|
||||
uiLengthDrawn: UINT ,
|
||||
}
|
||||
PDRAWTEXTPARAMS :: ^DRAWTEXTPARAMS
|
||||
|
||||
DrawTextFormat :: enum UINT {
|
||||
DT_TOP = 0x00000000,
|
||||
DT_LEFT = 0x00000000,
|
||||
DT_CENTER = 0x00000001,
|
||||
DT_RIGHT = 0x00000002,
|
||||
DT_VCENTER = 0x00000004,
|
||||
DT_BOTTOM = 0x00000008,
|
||||
DT_WORDBREAK = 0x00000010,
|
||||
DT_SINGLELINE = 0x00000020,
|
||||
DT_EXPANDTABS = 0x00000040,
|
||||
DT_TABSTOP = 0x00000080,
|
||||
DT_NOCLIP = 0x00000100,
|
||||
DT_EXTERNALLEADING = 0x00000200,
|
||||
DT_CALCRECT = 0x00000400,
|
||||
DT_NOPREFIX = 0x00000800,
|
||||
DT_INTERNAL = 0x00001000,
|
||||
DT_EDITCONTROL = 0x00002000,
|
||||
DT_PATH_ELLIPSIS = 0x00004000,
|
||||
DT_END_ELLIPSIS = 0x00008000,
|
||||
DT_MODIFYSTRING = 0x00010000,
|
||||
DT_RTLREADING = 0x00020000,
|
||||
DT_WORD_ELLIPSIS = 0x00040000,
|
||||
DT_NOFULLWIDTHCHARBREAK = 0x00080000,
|
||||
DT_HIDEPREFIX = 0x00100000,
|
||||
DT_PREFIXONLY = 0x00200000,
|
||||
}
|
||||
|
||||
RedrawWindowFlags :: enum UINT {
|
||||
RDW_INVALIDATE = 0x0001,
|
||||
RDW_INTERNALPAINT = 0x0002,
|
||||
RDW_ERASE = 0x0004,
|
||||
RDW_VALIDATE = 0x0008,
|
||||
RDW_NOINTERNALPAINT = 0x0010,
|
||||
RDW_NOERASE = 0x0020,
|
||||
RDW_NOCHILDREN = 0x0040,
|
||||
RDW_ALLCHILDREN = 0x0080,
|
||||
RDW_UPDATENOW = 0x0100,
|
||||
RDW_ERASENOW = 0x0200,
|
||||
RDW_FRAME = 0x0400,
|
||||
RDW_NOFRAME = 0x0800,
|
||||
}
|
||||
|
||||
@@ -11,6 +11,32 @@ foreign winmm {
|
||||
timeBeginPeriod :: proc(uPeriod: UINT) -> MMRESULT ---
|
||||
timeEndPeriod :: proc(uPeriod: UINT) -> MMRESULT ---
|
||||
timeGetTime :: proc() -> DWORD ---
|
||||
|
||||
waveOutGetNumDevs :: proc() -> UINT ---
|
||||
waveOutGetDevCapsW :: proc(uDeviceID: UINT_PTR, pwoc: LPWAVEOUTCAPSW, cbwoc: UINT) -> MMRESULT ---
|
||||
waveOutGetVolume :: proc(hwo: HWAVEOUT, pdwVolume: LPDWORD) -> MMRESULT ---
|
||||
waveOutSetVolume :: proc(hwo: HWAVEOUT, dwVolume: DWORD) -> MMRESULT ---
|
||||
waveOutGetErrorTextW :: proc(mmrError: MMRESULT, pszText: LPWSTR, cchText: UINT) -> MMRESULT ---
|
||||
waveOutOpen :: proc(phwo: LPHWAVEOUT, uDeviceID: UINT, pwfx: LPCWAVEFORMATEX, dwCallback: DWORD_PTR, dwInstance: DWORD_PTR, fdwOpen: DWORD) -> MMRESULT ---
|
||||
waveOutClose :: proc(hwo: HWAVEOUT) -> MMRESULT ---
|
||||
waveOutPrepareHeader :: proc(hwo: HWAVEOUT, pwh: LPWAVEHDR, cbwh: UINT) -> MMRESULT ---
|
||||
waveOutUnprepareHeader :: proc(hwo: HWAVEOUT, pwh: LPWAVEHDR, cbwh: UINT) -> MMRESULT ---
|
||||
waveOutWrite :: proc(hwo: HWAVEOUT, pwh: LPWAVEHDR, cbwh: UINT) -> MMRESULT ---
|
||||
waveOutPause :: proc(hwo: HWAVEOUT) -> MMRESULT ---
|
||||
waveOutRestart :: proc(hwo: HWAVEOUT) -> MMRESULT ---
|
||||
waveOutReset :: proc(hwo: HWAVEOUT) -> MMRESULT ---
|
||||
waveOutBreakLoop :: proc(hwo: HWAVEOUT) -> MMRESULT ---
|
||||
waveOutGetPosition :: proc(hwo: HWAVEOUT, pmmt: LPMMTIME, cbmmt: UINT) -> MMRESULT ---
|
||||
waveOutGetPitch :: proc(hwo: HWAVEOUT, pdwPitch: LPDWORD) -> MMRESULT ---
|
||||
waveOutSetPitch :: proc(hwo: HWAVEOUT, pdwPitch: DWORD) -> MMRESULT ---
|
||||
waveOutGetPlaybackRate :: proc(hwo: HWAVEOUT, pdwRate: LPDWORD) -> MMRESULT ---
|
||||
waveOutSetPlaybackRate :: proc(hwo: HWAVEOUT, pdwRate: DWORD) -> MMRESULT ---
|
||||
waveOutGetID :: proc(hwo: HWAVEOUT, puDeviceID: LPUINT) -> MMRESULT ---
|
||||
|
||||
waveInGetNumDevs :: proc() -> UINT ---
|
||||
waveInGetDevCapsW :: proc(uDeviceID: UINT_PTR, pwic: LPWAVEINCAPSW, cbwic: UINT) -> MMRESULT ---
|
||||
|
||||
PlaySoundW :: proc(pszSound: LPCWSTR, hmod: HMODULE, fdwSound: DWORD) -> BOOL ---
|
||||
}
|
||||
|
||||
LPTIMECAPS :: ^TIMECAPS
|
||||
@@ -169,4 +195,182 @@ MCIERR_NO_IDENTITY :: MCIERR_BASE + 94
|
||||
MIXERR_INVALLINE :: (MIXERR_BASE + 0)
|
||||
MIXERR_INVALCONTROL :: (MIXERR_BASE + 1)
|
||||
MIXERR_INVALVALUE :: (MIXERR_BASE + 2)
|
||||
MIXERR_LASTERROR :: (MIXERR_BASE + 2)
|
||||
MIXERR_LASTERROR :: (MIXERR_BASE + 2)
|
||||
|
||||
/* waveform output */
|
||||
MM_WOM_OPEN :: 0x3BB
|
||||
MM_WOM_CLOSE :: 0x3BC
|
||||
MM_WOM_DONE :: 0x3BD
|
||||
/* waveform input */
|
||||
MM_WIM_OPEN :: 0x3BE
|
||||
MM_WIM_CLOSE :: 0x3BF
|
||||
MM_WIM_DATA :: 0x3C0
|
||||
|
||||
WOM_OPEN :: MM_WOM_OPEN
|
||||
WOM_CLOSE :: MM_WOM_CLOSE
|
||||
WOM_DONE :: MM_WOM_DONE
|
||||
WIM_OPEN :: MM_WIM_OPEN
|
||||
WIM_CLOSE :: MM_WIM_CLOSE
|
||||
WIM_DATA :: MM_WIM_DATA
|
||||
|
||||
WAVE_MAPPER : UINT : 0xFFFFFFFF // -1
|
||||
|
||||
WAVE_FORMAT_QUERY :: 0x0001
|
||||
WAVE_ALLOWSYNC :: 0x0002
|
||||
WAVE_MAPPED :: 0x0004
|
||||
WAVE_FORMAT_DIRECT :: 0x0008
|
||||
WAVE_FORMAT_DIRECT_QUERY :: (WAVE_FORMAT_QUERY | WAVE_FORMAT_DIRECT)
|
||||
WAVE_MAPPED_DEFAULT_COMMUNICATION_DEVICE :: 0x0010
|
||||
|
||||
WHDR_DONE :: 0x00000001 /* done bit */
|
||||
WHDR_PREPARED :: 0x00000002 /* set if this header has been prepared */
|
||||
WHDR_BEGINLOOP :: 0x00000004 /* loop start block */
|
||||
WHDR_ENDLOOP :: 0x00000008 /* loop end block */
|
||||
WHDR_INQUEUE :: 0x00000010 /* reserved for driver */
|
||||
|
||||
WAVECAPS_PITCH :: 0x0001 /* supports pitch control */
|
||||
WAVECAPS_PLAYBACKRATE :: 0x0002 /* supports playback rate control */
|
||||
WAVECAPS_VOLUME :: 0x0004 /* supports volume control */
|
||||
WAVECAPS_LRVOLUME :: 0x0008 /* separate left-right volume control */
|
||||
WAVECAPS_SYNC :: 0x0010
|
||||
WAVECAPS_SAMPLEACCURATE :: 0x0020
|
||||
|
||||
WAVE_INVALIDFORMAT :: 0x00000000 /* invalid format */
|
||||
WAVE_FORMAT_1M08 :: 0x00000001 /* 11.025 kHz, Mono, 8-bit */
|
||||
WAVE_FORMAT_1S08 :: 0x00000002 /* 11.025 kHz, Stereo, 8-bit */
|
||||
WAVE_FORMAT_1M16 :: 0x00000004 /* 11.025 kHz, Mono, 16-bit */
|
||||
WAVE_FORMAT_1S16 :: 0x00000008 /* 11.025 kHz, Stereo, 16-bit */
|
||||
WAVE_FORMAT_2M08 :: 0x00000010 /* 22.05 kHz, Mono, 8-bit */
|
||||
WAVE_FORMAT_2S08 :: 0x00000020 /* 22.05 kHz, Stereo, 8-bit */
|
||||
WAVE_FORMAT_2M16 :: 0x00000040 /* 22.05 kHz, Mono, 16-bit */
|
||||
WAVE_FORMAT_2S16 :: 0x00000080 /* 22.05 kHz, Stereo, 16-bit */
|
||||
WAVE_FORMAT_4M08 :: 0x00000100 /* 44.1 kHz, Mono, 8-bit */
|
||||
WAVE_FORMAT_4S08 :: 0x00000200 /* 44.1 kHz, Stereo, 8-bit */
|
||||
WAVE_FORMAT_4M16 :: 0x00000400 /* 44.1 kHz, Mono, 16-bit */
|
||||
WAVE_FORMAT_4S16 :: 0x00000800 /* 44.1 kHz, Stereo, 16-bit */
|
||||
WAVE_FORMAT_44M08 :: 0x00000100 /* 44.1 kHz, Mono, 8-bit */
|
||||
WAVE_FORMAT_44S08 :: 0x00000200 /* 44.1 kHz, Stereo, 8-bit */
|
||||
WAVE_FORMAT_44M16 :: 0x00000400 /* 44.1 kHz, Mono, 16-bit */
|
||||
WAVE_FORMAT_44S16 :: 0x00000800 /* 44.1 kHz, Stereo, 16-bit */
|
||||
WAVE_FORMAT_48M08 :: 0x00001000 /* 48 kHz, Mono, 8-bit */
|
||||
WAVE_FORMAT_48S08 :: 0x00002000 /* 48 kHz, Stereo, 8-bit */
|
||||
WAVE_FORMAT_48M16 :: 0x00004000 /* 48 kHz, Mono, 16-bit */
|
||||
WAVE_FORMAT_48S16 :: 0x00008000 /* 48 kHz, Stereo, 16-bit */
|
||||
WAVE_FORMAT_96M08 :: 0x00010000 /* 96 kHz, Mono, 8-bit */
|
||||
WAVE_FORMAT_96S08 :: 0x00020000 /* 96 kHz, Stereo, 8-bit */
|
||||
WAVE_FORMAT_96M16 :: 0x00040000 /* 96 kHz, Mono, 16-bit */
|
||||
WAVE_FORMAT_96S16 :: 0x00080000 /* 96 kHz, Stereo, 16-bit */
|
||||
|
||||
HWAVE :: distinct HANDLE
|
||||
HWAVEIN :: distinct HANDLE
|
||||
HWAVEOUT :: distinct HANDLE
|
||||
|
||||
LPHWAVEIN :: ^HWAVEIN
|
||||
LPHWAVEOUT :: ^HWAVEOUT
|
||||
|
||||
// https://learn.microsoft.com/en-us/windows/win32/multimedia/multimedia-timer-structures
|
||||
MMTIME :: struct {
|
||||
wType: UINT,
|
||||
u: struct #raw_union {
|
||||
ms: DWORD,
|
||||
sample: DWORD,
|
||||
cb: DWORD,
|
||||
ticks: DWORD,
|
||||
smpte: struct {
|
||||
hour: BYTE,
|
||||
min: BYTE,
|
||||
sec: BYTE,
|
||||
frame: BYTE,
|
||||
fps: BYTE,
|
||||
dummy: BYTE,
|
||||
pad: [2]BYTE,
|
||||
},
|
||||
midi: struct {
|
||||
songptrpos: DWORD,
|
||||
},
|
||||
},
|
||||
}
|
||||
LPMMTIME :: ^MMTIME
|
||||
|
||||
MAXPNAMELEN :: 32
|
||||
MAXERRORLENGTH :: 256
|
||||
MMVERSION :: UINT
|
||||
|
||||
/* flags for wFormatTag field of WAVEFORMAT */
|
||||
WAVE_FORMAT_PCM :: 1
|
||||
|
||||
WAVEFORMATEX :: struct {
|
||||
wFormatTag: WORD,
|
||||
nChannels: WORD,
|
||||
nSamplesPerSec: DWORD,
|
||||
nAvgBytesPerSec: DWORD,
|
||||
nBlockAlign: WORD,
|
||||
wBitsPerSample: WORD,
|
||||
cbSize: WORD,
|
||||
}
|
||||
LPCWAVEFORMATEX :: ^WAVEFORMATEX
|
||||
|
||||
WAVEHDR :: struct {
|
||||
lpData: LPSTR, /* pointer to locked data buffer */
|
||||
dwBufferLength: DWORD, /* length of data buffer */
|
||||
dwBytesRecorded: DWORD, /* used for input only */
|
||||
dwUser: DWORD_PTR, /* for client's use */
|
||||
dwFlags: DWORD, /* assorted flags (see defines) */
|
||||
dwLoops: DWORD, /* loop control counter */
|
||||
lpNext: LPWAVEHDR, /* reserved for driver */
|
||||
reserved: DWORD_PTR, /* reserved for driver */
|
||||
}
|
||||
LPWAVEHDR :: ^WAVEHDR
|
||||
|
||||
WAVEINCAPSW :: struct {
|
||||
wMid: WORD, /* manufacturer ID */
|
||||
wPid: WORD, /* product ID */
|
||||
vDriverVersion: MMVERSION, /* version of the driver */
|
||||
szPname: [MAXPNAMELEN]WCHAR, /* product name (NULL terminated string) */
|
||||
dwFormats: DWORD, /* formats supported */
|
||||
wChannels: WORD, /* number of channels supported */
|
||||
wReserved1: WORD, /* structure packing */
|
||||
}
|
||||
LPWAVEINCAPSW :: ^WAVEINCAPSW
|
||||
|
||||
WAVEOUTCAPSW :: struct {
|
||||
wMid: WORD, /* manufacturer ID */
|
||||
wPid: WORD, /* product ID */
|
||||
vDriverVersion: MMVERSION, /* version of the driver */
|
||||
szPname: [MAXPNAMELEN]WCHAR, /* product name (NULL terminated string) */
|
||||
dwFormats: DWORD, /* formats supported */
|
||||
wChannels: WORD, /* number of sources supported */
|
||||
wReserved1: WORD, /* packing */
|
||||
dwSupport: DWORD, /* functionality supported by driver */
|
||||
}
|
||||
LPWAVEOUTCAPSW :: ^WAVEOUTCAPSW
|
||||
|
||||
// flag values for PlaySound
|
||||
SND_SYNC :: 0x0000 /* play synchronously (default) */
|
||||
SND_ASYNC :: 0x0001 /* play asynchronously */
|
||||
SND_NODEFAULT :: 0x0002 /* silence (!default) if sound not found */
|
||||
SND_MEMORY :: 0x0004 /* pszSound points to a memory file */
|
||||
SND_LOOP :: 0x0008 /* loop the sound until next sndPlaySound */
|
||||
SND_NOSTOP :: 0x0010 /* don't stop any currently playing sound */
|
||||
|
||||
SND_NOWAIT :: 0x00002000 /* don't wait if the driver is busy */
|
||||
SND_ALIAS :: 0x00010000 /* name is a registry alias */
|
||||
SND_ALIAS_ID :: 0x00110000 /* alias is a predefined ID */
|
||||
SND_FILENAME :: 0x00020000 /* name is file name */
|
||||
SND_RESOURCE :: 0x00040004 /* name is resource name or atom */
|
||||
|
||||
SND_PURGE :: 0x0040 /* purge non-static events for task */
|
||||
SND_APPLICATION :: 0x0080 /* look for application specific association */
|
||||
|
||||
SND_SENTRY :: 0x00080000 /* Generate a SoundSentry event with this sound */
|
||||
SND_RING :: 0x00100000 /* Treat this as a "ring" from a communications app - don't duck me */
|
||||
SND_SYSTEM :: 0x00200000 /* Treat this as a system sound */
|
||||
|
||||
|
||||
CALLBACK_TYPEMASK :: 0x00070000 /* callback type mask */
|
||||
CALLBACK_NULL :: 0x00000000 /* no callback */
|
||||
CALLBACK_WINDOW :: 0x00010000 /* dwCallback is a HWND */
|
||||
CALLBACK_TASK :: 0x00020000 /* dwCallback is a HTASK */
|
||||
CALLBACK_FUNCTION :: 0x00030000 /* dwCallback is a FARPROC */
|
||||
CALLBACK_THREAD :: CALLBACK_TASK /* thread ID replaces 16 bit task */
|
||||
CALLBACK_EVENT :: 0x00050000 /* dwCallback is an EVENT Handle */
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// +build linux, darwin, freebsd, openbsd
|
||||
// +build linux, darwin, freebsd, openbsd, haiku
|
||||
// +private
|
||||
package thread
|
||||
|
||||
@@ -78,7 +78,9 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
|
||||
|
||||
// NOTE(tetra, 2019-11-01): These only fail if their argument is invalid.
|
||||
assert(unix.pthread_attr_setdetachstate(&attrs, unix.PTHREAD_CREATE_JOINABLE) == 0)
|
||||
assert(unix.pthread_attr_setinheritsched(&attrs, unix.PTHREAD_EXPLICIT_SCHED) == 0)
|
||||
when ODIN_OS != .Haiku {
|
||||
assert(unix.pthread_attr_setinheritsched(&attrs, unix.PTHREAD_EXPLICIT_SCHED) == 0)
|
||||
}
|
||||
|
||||
thread := new(Thread)
|
||||
if thread == nil {
|
||||
@@ -88,8 +90,11 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
|
||||
|
||||
// Set thread priority.
|
||||
policy: i32
|
||||
res := unix.pthread_attr_getschedpolicy(&attrs, &policy)
|
||||
assert(res == 0)
|
||||
res: i32
|
||||
when ODIN_OS != .Haiku {
|
||||
res = unix.pthread_attr_getschedpolicy(&attrs, &policy)
|
||||
assert(res == 0)
|
||||
}
|
||||
params: unix.sched_param
|
||||
res = unix.pthread_attr_getschedparam(&attrs, ¶ms)
|
||||
assert(res == 0)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//+private
|
||||
//+build linux, darwin, freebsd, openbsd
|
||||
//+build linux, darwin, freebsd, openbsd, haiku
|
||||
package time
|
||||
|
||||
import "core:sys/unix"
|
||||
|
||||
@@ -18,6 +18,7 @@ enum TargetOsKind : u16 {
|
||||
TargetOs_essence,
|
||||
TargetOs_freebsd,
|
||||
TargetOs_openbsd,
|
||||
TargetOs_haiku,
|
||||
|
||||
TargetOs_wasi,
|
||||
TargetOs_js,
|
||||
@@ -78,6 +79,7 @@ gb_global String target_os_names[TargetOs_COUNT] = {
|
||||
str_lit("essence"),
|
||||
str_lit("freebsd"),
|
||||
str_lit("openbsd"),
|
||||
str_lit("haiku"),
|
||||
|
||||
str_lit("wasi"),
|
||||
str_lit("js"),
|
||||
@@ -542,6 +544,13 @@ gb_global TargetMetrics target_openbsd_amd64 = {
|
||||
str_lit("e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"),
|
||||
};
|
||||
|
||||
gb_global TargetMetrics target_haiku_amd64 = {
|
||||
TargetOs_haiku,
|
||||
TargetArch_amd64,
|
||||
8, 8, 8, 16,
|
||||
str_lit("x86_64-unknown-haiku"),
|
||||
};
|
||||
|
||||
gb_global TargetMetrics target_essence_amd64 = {
|
||||
TargetOs_essence,
|
||||
TargetArch_amd64,
|
||||
@@ -641,6 +650,7 @@ gb_global NamedTargetMetrics named_targets[] = {
|
||||
{ str_lit("freebsd_amd64"), &target_freebsd_amd64 },
|
||||
|
||||
{ str_lit("openbsd_amd64"), &target_openbsd_amd64 },
|
||||
{ str_lit("haiku_amd64"), &target_haiku_amd64 },
|
||||
|
||||
{ str_lit("freestanding_wasm32"), &target_freestanding_wasm32 },
|
||||
{ str_lit("wasi_wasm32"), &target_wasi_wasm32 },
|
||||
@@ -872,6 +882,58 @@ gb_internal String internal_odin_root_dir(void) {
|
||||
return path;
|
||||
}
|
||||
|
||||
#elif defined(GB_SYSTEM_HAIKU)
|
||||
|
||||
#include <FindDirectory.h>
|
||||
|
||||
gb_internal String path_to_fullpath(gbAllocator a, String s, bool *ok_);
|
||||
|
||||
gb_internal String internal_odin_root_dir(void) {
|
||||
String path = global_module_path;
|
||||
isize len, i;
|
||||
u8 *text;
|
||||
|
||||
if (global_module_path_set) {
|
||||
return global_module_path;
|
||||
}
|
||||
|
||||
auto path_buf = array_make<char>(heap_allocator(), 300);
|
||||
defer (array_free(&path_buf));
|
||||
|
||||
len = 0;
|
||||
for (;;) {
|
||||
u32 sz = path_buf.count;
|
||||
int res = find_path(B_APP_IMAGE_SYMBOL, B_FIND_PATH_IMAGE_PATH, nullptr, &path_buf[0], sz);
|
||||
if(res == B_OK) {
|
||||
len = sz;
|
||||
break;
|
||||
} else {
|
||||
array_resize(&path_buf, sz + 1);
|
||||
}
|
||||
}
|
||||
|
||||
mutex_lock(&string_buffer_mutex);
|
||||
defer (mutex_unlock(&string_buffer_mutex));
|
||||
|
||||
text = gb_alloc_array(permanent_allocator(), u8, len + 1);
|
||||
gb_memmove(text, &path_buf[0], len);
|
||||
|
||||
path = path_to_fullpath(heap_allocator(), make_string(text, len), nullptr);
|
||||
|
||||
for (i = path.len-1; i >= 0; i--) {
|
||||
u8 c = path[i];
|
||||
if (c == '/' || c == '\\') {
|
||||
break;
|
||||
}
|
||||
path.len--;
|
||||
}
|
||||
|
||||
global_module_path = path;
|
||||
global_module_path_set = true;
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
#elif defined(GB_SYSTEM_OSX)
|
||||
|
||||
#include <mach-o/dyld.h>
|
||||
@@ -888,6 +950,7 @@ gb_internal String internal_odin_root_dir(void) {
|
||||
}
|
||||
|
||||
auto path_buf = array_make<char>(heap_allocator(), 300);
|
||||
defer (array_free(&path_buf));
|
||||
|
||||
len = 0;
|
||||
for (;;) {
|
||||
@@ -920,9 +983,6 @@ gb_internal String internal_odin_root_dir(void) {
|
||||
global_module_path = path;
|
||||
global_module_path_set = true;
|
||||
|
||||
|
||||
// array_free(&path_buf);
|
||||
|
||||
return path;
|
||||
}
|
||||
#else
|
||||
@@ -1301,6 +1361,8 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
|
||||
metrics = &target_freebsd_amd64;
|
||||
#elif defined(GB_SYSTEM_OPENBSD)
|
||||
metrics = &target_openbsd_amd64;
|
||||
#elif defined(GB_SYSTEM_HAIKU)
|
||||
metrics = &target_haiku_amd64;
|
||||
#elif defined(GB_CPU_ARM)
|
||||
metrics = &target_linux_arm64;
|
||||
#else
|
||||
@@ -1405,6 +1467,9 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
|
||||
case TargetOs_openbsd:
|
||||
bc->link_flags = str_lit("-arch x86-64 ");
|
||||
break;
|
||||
case TargetOs_haiku:
|
||||
bc->link_flags = str_lit("-arch x86-64 ");
|
||||
break;
|
||||
}
|
||||
} else if (bc->metrics.arch == TargetArch_i386) {
|
||||
switch (bc->metrics.os) {
|
||||
|
||||
@@ -4928,6 +4928,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
case TargetOs_essence:
|
||||
case TargetOs_freebsd:
|
||||
case TargetOs_openbsd:
|
||||
case TargetOs_haiku:
|
||||
switch (build_context.metrics.arch) {
|
||||
case TargetArch_i386:
|
||||
case TargetArch_amd64:
|
||||
|
||||
@@ -1010,6 +1010,7 @@ gb_internal void init_universal(void) {
|
||||
{"Linux", TargetOs_linux},
|
||||
{"Essence", TargetOs_essence},
|
||||
{"FreeBSD", TargetOs_freebsd},
|
||||
{"Haiku", TargetOs_haiku},
|
||||
{"OpenBSD", TargetOs_openbsd},
|
||||
{"WASI", TargetOs_wasi},
|
||||
{"JS", TargetOs_js},
|
||||
|
||||
49
src/gb/gb.h
49
src/gb/gb.h
@@ -83,6 +83,10 @@ extern "C" {
|
||||
#ifndef GB_SYSTEM_OPENBSD
|
||||
#define GB_SYSTEM_OPENBSD 1
|
||||
#endif
|
||||
#elif defined(__HAIKU__) || defined(__haiku__)
|
||||
#ifndef GB_SYSTEM_HAIKU
|
||||
#define GB_SYSTEM_HAIKU 1
|
||||
#endif
|
||||
#else
|
||||
#error This UNIX operating system is not supported
|
||||
#endif
|
||||
@@ -206,7 +210,7 @@ extern "C" {
|
||||
#endif
|
||||
#include <stdlib.h> // NOTE(bill): malloc on linux
|
||||
#include <sys/mman.h>
|
||||
#if !defined(GB_SYSTEM_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__)
|
||||
#if !defined(GB_SYSTEM_OSX) && !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__HAIKU__)
|
||||
#include <sys/sendfile.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
@@ -247,6 +251,13 @@ extern "C" {
|
||||
#include <pthread_np.h>
|
||||
#define lseek64 lseek
|
||||
#endif
|
||||
|
||||
#if defined(GB_SYSTEM_HAIKU)
|
||||
#include <stdio.h>
|
||||
#include <pthread.h>
|
||||
#include <kernel/OS.h>
|
||||
#define lseek64 lseek
|
||||
#endif
|
||||
|
||||
#if defined(GB_SYSTEM_UNIX)
|
||||
#include <semaphore.h>
|
||||
@@ -801,6 +812,13 @@ typedef struct gbAffinity {
|
||||
isize thread_count;
|
||||
isize threads_per_core;
|
||||
} gbAffinity;
|
||||
#elif defined(GB_SYSTEM_HAIKU)
|
||||
typedef struct gbAffinity {
|
||||
b32 is_accurate;
|
||||
isize core_count;
|
||||
isize thread_count;
|
||||
isize threads_per_core;
|
||||
} gbAffinity;
|
||||
#else
|
||||
#error TODO(bill): Unknown system
|
||||
#endif
|
||||
@@ -2984,6 +3002,8 @@ gb_inline u32 gb_thread_current_id(void) {
|
||||
__asm__("mov %%fs:0x10,%0" : "=r"(thread_id));
|
||||
#elif defined(GB_SYSTEM_LINUX)
|
||||
thread_id = gettid();
|
||||
#elif defined(GB_SYSTEM_HAIKU)
|
||||
thread_id = find_thread(NULL);
|
||||
#else
|
||||
#error Unsupported architecture for gb_thread_current_id()
|
||||
#endif
|
||||
@@ -3184,7 +3204,9 @@ b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) {
|
||||
//info.affinity_tag = cast(integer_t)index;
|
||||
//result = thread_policy_set(thread, THREAD_AFFINITY_POLICY, cast(thread_policy_t)&info, THREAD_AFFINITY_POLICY_COUNT);
|
||||
|
||||
#if !defined(GB_SYSTEM_HAIKU)
|
||||
result = pthread_setaffinity_np(thread, sizeof(cpuset_t), &mn);
|
||||
#endif
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
@@ -3236,6 +3258,29 @@ b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) {
|
||||
return true;
|
||||
}
|
||||
|
||||
isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) {
|
||||
GB_ASSERT(0 <= core && core < a->core_count);
|
||||
return a->threads_per_core;
|
||||
}
|
||||
#elif defined(GB_SYSTEM_HAIKU)
|
||||
#include <unistd.h>
|
||||
|
||||
void gb_affinity_init(gbAffinity *a) {
|
||||
a->core_count = sysconf(_SC_NPROCESSORS_ONLN);
|
||||
a->threads_per_core = 1;
|
||||
a->is_accurate = a->core_count > 0;
|
||||
a->core_count = a->is_accurate ? a->core_count : 1;
|
||||
a->thread_count = a->core_count;
|
||||
}
|
||||
|
||||
void gb_affinity_destroy(gbAffinity *a) {
|
||||
gb_unused(a);
|
||||
}
|
||||
|
||||
b32 gb_affinity_set(gbAffinity *a, isize core, isize thread_index) {
|
||||
return true;
|
||||
}
|
||||
|
||||
isize gb_affinity_thread_count_for_core(gbAffinity *a, isize core) {
|
||||
GB_ASSERT(0 <= core && core < a->core_count);
|
||||
return a->threads_per_core;
|
||||
@@ -5457,7 +5502,7 @@ gb_inline b32 gb_file_copy(char const *existing_filename, char const *new_filena
|
||||
}
|
||||
}
|
||||
|
||||
gb_free(buf);
|
||||
gb_mfree(buf);
|
||||
close(new_fd);
|
||||
close(existing_fd);
|
||||
|
||||
|
||||
@@ -474,8 +474,8 @@ gb_internal i32 linker_stage(LinkerData *gen) {
|
||||
link_settings = gb_string_appendc(link_settings, "-Wl,-fini,'_odin_exit_point' ");
|
||||
}
|
||||
|
||||
} else if (build_context.metrics.os != TargetOs_openbsd) {
|
||||
// OpenBSD defaults to PIE executable. do not pass -no-pie for it.
|
||||
} else if (build_context.metrics.os != TargetOs_openbsd && build_context.metrics.os != TargetOs_haiku) {
|
||||
// OpenBSD and Haiku default to PIE executable. do not pass -no-pie for it.
|
||||
link_settings = gb_string_appendc(link_settings, "-no-pie ");
|
||||
}
|
||||
|
||||
|
||||
@@ -2564,8 +2564,8 @@ gb_internal bool lb_generate_code(lbGenerator *gen) {
|
||||
|
||||
switch (build_context.reloc_mode) {
|
||||
case RelocMode_Default:
|
||||
if (build_context.metrics.os == TargetOs_openbsd) {
|
||||
// Always use PIC for OpenBSD: it defaults to PIE
|
||||
if (build_context.metrics.os == TargetOs_openbsd || build_context.metrics.os == TargetOs_haiku) {
|
||||
// Always use PIC for OpenBSD and Haiku: they default to PIE
|
||||
reloc_mode = LLVMRelocPIC;
|
||||
}
|
||||
break;
|
||||
|
||||
922
src/path.cpp
922
src/path.cpp
@@ -1,461 +1,461 @@
|
||||
/*
|
||||
Path handling utilities.
|
||||
*/
|
||||
#if !defined(GB_SYSTEM_WINDOWS)
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
gb_internal String remove_extension_from_path(String const &s) {
|
||||
if (s.len != 0 && s.text[s.len-1] == '.') {
|
||||
return s;
|
||||
}
|
||||
for (isize i = s.len-1; i >= 0; i--) {
|
||||
if (s[i] == '.') {
|
||||
return substring(s, 0, i);
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
gb_internal String remove_directory_from_path(String const &s) {
|
||||
isize len = 0;
|
||||
for (isize i = s.len-1; i >= 0; i--) {
|
||||
if (s[i] == '/' ||
|
||||
s[i] == '\\') {
|
||||
break;
|
||||
}
|
||||
len += 1;
|
||||
}
|
||||
return substring(s, s.len-len, s.len);
|
||||
}
|
||||
|
||||
|
||||
// NOTE(Mark Naughton): getcwd as String
|
||||
#if !defined(GB_SYSTEM_WINDOWS)
|
||||
gb_internal String get_current_directory(void) {
|
||||
char cwd[256];
|
||||
getcwd(cwd, 256);
|
||||
|
||||
return make_string_c(cwd);
|
||||
}
|
||||
|
||||
#else
|
||||
gb_internal String get_current_directory(void) {
|
||||
gbAllocator a = heap_allocator();
|
||||
|
||||
wchar_t cwd[256];
|
||||
GetCurrentDirectoryW(256, cwd);
|
||||
|
||||
String16 wstr = make_string16_c(cwd);
|
||||
|
||||
return string16_to_string(a, wstr);
|
||||
}
|
||||
#endif
|
||||
|
||||
gb_internal bool path_is_directory(String path);
|
||||
|
||||
gb_internal String directory_from_path(String const &s) {
|
||||
if (path_is_directory(s)) {
|
||||
return s;
|
||||
}
|
||||
|
||||
isize i = s.len-1;
|
||||
for (; i >= 0; i--) {
|
||||
if (s[i] == '/' ||
|
||||
s[i] == '\\') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i >= 0) {
|
||||
return substring(s, 0, i);
|
||||
}
|
||||
return substring(s, 0, 0);
|
||||
}
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
gb_internal bool path_is_directory(String path) {
|
||||
gbAllocator a = heap_allocator();
|
||||
String16 wstr = string_to_string16(a, path);
|
||||
defer (gb_free(a, wstr.text));
|
||||
|
||||
i32 attribs = GetFileAttributesW(wstr.text);
|
||||
if (attribs < 0) return false;
|
||||
|
||||
return (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||
}
|
||||
|
||||
#else
|
||||
gb_internal bool path_is_directory(String path) {
|
||||
gbAllocator a = heap_allocator();
|
||||
char *copy = cast(char *)copy_string(a, path).text;
|
||||
defer (gb_free(a, copy));
|
||||
|
||||
struct stat s;
|
||||
if (stat(copy, &s) == 0) {
|
||||
return (s.st_mode & S_IFDIR) != 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
gb_internal String path_to_full_path(gbAllocator a, String path) {
|
||||
gbAllocator ha = heap_allocator();
|
||||
char *path_c = gb_alloc_str_len(ha, cast(char *)path.text, path.len);
|
||||
defer (gb_free(ha, path_c));
|
||||
|
||||
char *fullpath = gb_path_get_full_name(a, path_c);
|
||||
String res = string_trim_whitespace(make_string_c(fullpath));
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
for (isize i = 0; i < res.len; i++) {
|
||||
if (res.text[i] == '\\') {
|
||||
res.text[i] = '/';
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return copy_string(a, res);
|
||||
}
|
||||
|
||||
struct Path {
|
||||
String basename;
|
||||
String name;
|
||||
String ext;
|
||||
};
|
||||
|
||||
// NOTE(Jeroen): Naively turns a Path into a string.
|
||||
gb_internal String path_to_string(gbAllocator a, Path path) {
|
||||
if (path.basename.len + path.name.len + path.ext.len == 0) {
|
||||
return make_string(nullptr, 0);
|
||||
}
|
||||
|
||||
isize len = path.basename.len + 1 + path.name.len + 1;
|
||||
if (path.ext.len > 0) {
|
||||
len += path.ext.len + 1;
|
||||
}
|
||||
|
||||
u8 *str = gb_alloc_array(a, u8, len);
|
||||
|
||||
isize i = 0;
|
||||
gb_memmove(str+i, path.basename.text, path.basename.len); i += path.basename.len;
|
||||
|
||||
gb_memmove(str+i, "/", 1); i += 1;
|
||||
|
||||
gb_memmove(str+i, path.name.text, path.name.len); i += path.name.len;
|
||||
if (path.ext.len > 0) {
|
||||
gb_memmove(str+i, ".", 1); i += 1;
|
||||
gb_memmove(str+i, path.ext.text, path.ext.len); i += path.ext.len;
|
||||
}
|
||||
str[i] = 0;
|
||||
|
||||
String res = make_string(str, i);
|
||||
res = string_trim_whitespace(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
// NOTE(Jeroen): Naively turns a Path into a string, then normalizes it using `path_to_full_path`.
|
||||
gb_internal String path_to_full_path(gbAllocator a, Path path) {
|
||||
String temp = path_to_string(heap_allocator(), path);
|
||||
defer (gb_free(heap_allocator(), temp.text));
|
||||
|
||||
return path_to_full_path(a, temp);
|
||||
}
|
||||
|
||||
// NOTE(Jeroen): Takes a path like "odin" or "W:\Odin", turns it into a full path,
|
||||
// and then breaks it into its components to make a Path.
|
||||
gb_internal Path path_from_string(gbAllocator a, String const &path) {
|
||||
Path res = {};
|
||||
|
||||
if (path.len == 0) return res;
|
||||
|
||||
String fullpath = path_to_full_path(a, path);
|
||||
defer (gb_free(heap_allocator(), fullpath.text));
|
||||
|
||||
res.basename = directory_from_path(fullpath);
|
||||
res.basename = copy_string(a, res.basename);
|
||||
|
||||
if (path_is_directory(fullpath)) {
|
||||
// It's a directory. We don't need to tinker with the name and extension.
|
||||
// It could have a superfluous trailing `/`. Remove it if so.
|
||||
if (res.basename.len > 0 && res.basename.text[res.basename.len - 1] == '/') {
|
||||
res.basename.len--;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// Note(Dragos): Is the copy_string required if it's a substring?
|
||||
isize name_start = (res.basename.len > 0) ? res.basename.len + 1 : res.basename.len;
|
||||
res.name = substring(fullpath, name_start, fullpath.len);
|
||||
res.name = remove_extension_from_path(res.name);
|
||||
res.name = copy_string(a, res.name);
|
||||
|
||||
res.ext = path_extension(fullpath, false); // false says not to include the dot.
|
||||
res.ext = copy_string(a, res.ext);
|
||||
return res;
|
||||
}
|
||||
|
||||
// NOTE(Jeroen): Takes a path String and returns the last path element.
|
||||
gb_internal String last_path_element(String const &path) {
|
||||
isize count = 0;
|
||||
u8 * start = (u8 *)(&path.text[path.len - 1]);
|
||||
for (isize length = path.len; length > 0 && path.text[length - 1] != '/'; length--) {
|
||||
count++;
|
||||
start--;
|
||||
}
|
||||
if (count > 0) {
|
||||
start++; // Advance past the `/` and return the substring.
|
||||
String res = make_string(start, count);
|
||||
return res;
|
||||
}
|
||||
// Must be a root path like `/` or `C:/`, return empty String.
|
||||
return STR_LIT("");
|
||||
}
|
||||
|
||||
gb_internal bool path_is_directory(Path path) {
|
||||
String path_string = path_to_full_path(heap_allocator(), path);
|
||||
defer (gb_free(heap_allocator(), path_string.text));
|
||||
|
||||
return path_is_directory(path_string);
|
||||
}
|
||||
|
||||
struct FileInfo {
|
||||
String name;
|
||||
String fullpath;
|
||||
i64 size;
|
||||
bool is_dir;
|
||||
};
|
||||
|
||||
enum ReadDirectoryError {
|
||||
ReadDirectory_None,
|
||||
|
||||
ReadDirectory_InvalidPath,
|
||||
ReadDirectory_NotExists,
|
||||
ReadDirectory_Permission,
|
||||
ReadDirectory_NotDir,
|
||||
ReadDirectory_Empty,
|
||||
ReadDirectory_Unknown,
|
||||
|
||||
ReadDirectory_COUNT,
|
||||
};
|
||||
|
||||
gb_internal i64 get_file_size(String path) {
|
||||
char *c_str = alloc_cstring(heap_allocator(), path);
|
||||
defer (gb_free(heap_allocator(), c_str));
|
||||
|
||||
gbFile f = {};
|
||||
gbFileError err = gb_file_open(&f, c_str);
|
||||
defer (gb_file_close(&f));
|
||||
if (err != gbFileError_None) {
|
||||
return -1;
|
||||
}
|
||||
return gb_file_size(&f);
|
||||
}
|
||||
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
|
||||
GB_ASSERT(fi != nullptr);
|
||||
|
||||
|
||||
while (path.len > 0) {
|
||||
Rune end = path[path.len-1];
|
||||
if (end == '/') {
|
||||
path.len -= 1;
|
||||
} else if (end == '\\') {
|
||||
path.len -= 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (path.len == 0) {
|
||||
return ReadDirectory_InvalidPath;
|
||||
}
|
||||
{
|
||||
char *c_str = alloc_cstring(temporary_allocator(), path);
|
||||
gbFile f = {};
|
||||
gbFileError file_err = gb_file_open(&f, c_str);
|
||||
defer (gb_file_close(&f));
|
||||
|
||||
switch (file_err) {
|
||||
case gbFileError_Invalid: return ReadDirectory_InvalidPath;
|
||||
case gbFileError_NotExists: return ReadDirectory_NotExists;
|
||||
// case gbFileError_Permission: return ReadDirectory_Permission;
|
||||
}
|
||||
}
|
||||
|
||||
if (!path_is_directory(path)) {
|
||||
return ReadDirectory_NotDir;
|
||||
}
|
||||
|
||||
|
||||
gbAllocator a = heap_allocator();
|
||||
char *new_path = gb_alloc_array(a, char, path.len+3);
|
||||
defer (gb_free(a, new_path));
|
||||
|
||||
gb_memmove(new_path, path.text, path.len);
|
||||
gb_memmove(new_path+path.len, "/*", 2);
|
||||
new_path[path.len+2] = 0;
|
||||
|
||||
String np = make_string(cast(u8 *)new_path, path.len+2);
|
||||
String16 wstr = string_to_string16(a, np);
|
||||
defer (gb_free(a, wstr.text));
|
||||
|
||||
WIN32_FIND_DATAW file_data = {};
|
||||
HANDLE find_file = FindFirstFileW(wstr.text, &file_data);
|
||||
if (find_file == INVALID_HANDLE_VALUE) {
|
||||
return ReadDirectory_Unknown;
|
||||
}
|
||||
defer (FindClose(find_file));
|
||||
|
||||
array_init(fi, a, 0, 100);
|
||||
|
||||
do {
|
||||
wchar_t *filename_w = file_data.cFileName;
|
||||
u64 size = cast(u64)file_data.nFileSizeLow;
|
||||
size |= (cast(u64)file_data.nFileSizeHigh) << 32;
|
||||
String name = string16_to_string(a, make_string16_c(filename_w));
|
||||
if (name == "." || name == "..") {
|
||||
gb_free(a, name.text);
|
||||
continue;
|
||||
}
|
||||
|
||||
String filepath = {};
|
||||
filepath.len = path.len+1+name.len;
|
||||
filepath.text = gb_alloc_array(a, u8, filepath.len+1);
|
||||
defer (gb_free(a, filepath.text));
|
||||
gb_memmove(filepath.text, path.text, path.len);
|
||||
gb_memmove(filepath.text+path.len, "/", 1);
|
||||
gb_memmove(filepath.text+path.len+1, name.text, name.len);
|
||||
|
||||
FileInfo info = {};
|
||||
info.name = name;
|
||||
info.fullpath = path_to_full_path(a, filepath);
|
||||
info.size = cast(i64)size;
|
||||
info.is_dir = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||
array_add(fi, info);
|
||||
} while (FindNextFileW(find_file, &file_data));
|
||||
|
||||
if (fi->count == 0) {
|
||||
return ReadDirectory_Empty;
|
||||
}
|
||||
|
||||
return ReadDirectory_None;
|
||||
}
|
||||
#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD)
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
|
||||
GB_ASSERT(fi != nullptr);
|
||||
|
||||
gbAllocator a = heap_allocator();
|
||||
|
||||
char *c_path = alloc_cstring(a, path);
|
||||
defer (gb_free(a, c_path));
|
||||
|
||||
DIR *dir = opendir(c_path);
|
||||
if (!dir) {
|
||||
switch (errno) {
|
||||
case ENOENT:
|
||||
return ReadDirectory_NotExists;
|
||||
case EACCES:
|
||||
return ReadDirectory_Permission;
|
||||
case ENOTDIR:
|
||||
return ReadDirectory_NotDir;
|
||||
default:
|
||||
// ENOMEM: out of memory
|
||||
// EMFILE: per-process limit on open fds reached
|
||||
// ENFILE: system-wide limit on total open files reached
|
||||
return ReadDirectory_Unknown;
|
||||
}
|
||||
GB_PANIC("unreachable");
|
||||
}
|
||||
|
||||
array_init(fi, a, 0, 100);
|
||||
|
||||
for (;;) {
|
||||
struct dirent *entry = readdir(dir);
|
||||
if (entry == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
String name = make_string_c(entry->d_name);
|
||||
if (name == "." || name == "..") {
|
||||
continue;
|
||||
}
|
||||
|
||||
String filepath = {};
|
||||
filepath.len = path.len+1+name.len;
|
||||
filepath.text = gb_alloc_array(a, u8, filepath.len+1);
|
||||
defer (gb_free(a, filepath.text));
|
||||
gb_memmove(filepath.text, path.text, path.len);
|
||||
gb_memmove(filepath.text+path.len, "/", 1);
|
||||
gb_memmove(filepath.text+path.len+1, name.text, name.len);
|
||||
filepath.text[filepath.len] = 0;
|
||||
|
||||
|
||||
struct stat dir_stat = {};
|
||||
|
||||
if (stat((char *)filepath.text, &dir_stat)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (S_ISDIR(dir_stat.st_mode)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
i64 size = dir_stat.st_size;
|
||||
|
||||
FileInfo info = {};
|
||||
info.name = name;
|
||||
info.fullpath = path_to_full_path(a, filepath);
|
||||
info.size = size;
|
||||
array_add(fi, info);
|
||||
}
|
||||
|
||||
if (fi->count == 0) {
|
||||
return ReadDirectory_Empty;
|
||||
}
|
||||
|
||||
return ReadDirectory_None;
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
#error Implement read_directory
|
||||
#endif
|
||||
|
||||
#if !defined(GB_SYSTEM_WINDOWS)
|
||||
gb_internal bool write_directory(String path) {
|
||||
char const *pathname = (char *) path.text;
|
||||
|
||||
if (access(pathname, W_OK) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
gb_internal bool write_directory(String path) {
|
||||
String16 wstr = string_to_string16(heap_allocator(), path);
|
||||
LPCWSTR wdirectory_name = wstr.text;
|
||||
|
||||
HANDLE directory = CreateFileW(wdirectory_name,
|
||||
GENERIC_WRITE,
|
||||
0,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS,
|
||||
NULL);
|
||||
|
||||
if (directory == INVALID_HANDLE_VALUE) {
|
||||
DWORD error_code = GetLastError();
|
||||
if (error_code == ERROR_ACCESS_DENIED) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(directory);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
Path handling utilities.
|
||||
*/
|
||||
#if !defined(GB_SYSTEM_WINDOWS)
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
gb_internal String remove_extension_from_path(String const &s) {
|
||||
if (s.len != 0 && s.text[s.len-1] == '.') {
|
||||
return s;
|
||||
}
|
||||
for (isize i = s.len-1; i >= 0; i--) {
|
||||
if (s[i] == '.') {
|
||||
return substring(s, 0, i);
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
gb_internal String remove_directory_from_path(String const &s) {
|
||||
isize len = 0;
|
||||
for (isize i = s.len-1; i >= 0; i--) {
|
||||
if (s[i] == '/' ||
|
||||
s[i] == '\\') {
|
||||
break;
|
||||
}
|
||||
len += 1;
|
||||
}
|
||||
return substring(s, s.len-len, s.len);
|
||||
}
|
||||
|
||||
|
||||
// NOTE(Mark Naughton): getcwd as String
|
||||
#if !defined(GB_SYSTEM_WINDOWS)
|
||||
gb_internal String get_current_directory(void) {
|
||||
char cwd[256];
|
||||
getcwd(cwd, 256);
|
||||
|
||||
return make_string_c(cwd);
|
||||
}
|
||||
|
||||
#else
|
||||
gb_internal String get_current_directory(void) {
|
||||
gbAllocator a = heap_allocator();
|
||||
|
||||
wchar_t cwd[256];
|
||||
GetCurrentDirectoryW(256, cwd);
|
||||
|
||||
String16 wstr = make_string16_c(cwd);
|
||||
|
||||
return string16_to_string(a, wstr);
|
||||
}
|
||||
#endif
|
||||
|
||||
gb_internal bool path_is_directory(String path);
|
||||
|
||||
gb_internal String directory_from_path(String const &s) {
|
||||
if (path_is_directory(s)) {
|
||||
return s;
|
||||
}
|
||||
|
||||
isize i = s.len-1;
|
||||
for (; i >= 0; i--) {
|
||||
if (s[i] == '/' ||
|
||||
s[i] == '\\') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i >= 0) {
|
||||
return substring(s, 0, i);
|
||||
}
|
||||
return substring(s, 0, 0);
|
||||
}
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
gb_internal bool path_is_directory(String path) {
|
||||
gbAllocator a = heap_allocator();
|
||||
String16 wstr = string_to_string16(a, path);
|
||||
defer (gb_free(a, wstr.text));
|
||||
|
||||
i32 attribs = GetFileAttributesW(wstr.text);
|
||||
if (attribs < 0) return false;
|
||||
|
||||
return (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||
}
|
||||
|
||||
#else
|
||||
gb_internal bool path_is_directory(String path) {
|
||||
gbAllocator a = heap_allocator();
|
||||
char *copy = cast(char *)copy_string(a, path).text;
|
||||
defer (gb_free(a, copy));
|
||||
|
||||
struct stat s;
|
||||
if (stat(copy, &s) == 0) {
|
||||
return (s.st_mode & S_IFDIR) != 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
gb_internal String path_to_full_path(gbAllocator a, String path) {
|
||||
gbAllocator ha = heap_allocator();
|
||||
char *path_c = gb_alloc_str_len(ha, cast(char *)path.text, path.len);
|
||||
defer (gb_free(ha, path_c));
|
||||
|
||||
char *fullpath = gb_path_get_full_name(a, path_c);
|
||||
String res = string_trim_whitespace(make_string_c(fullpath));
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
for (isize i = 0; i < res.len; i++) {
|
||||
if (res.text[i] == '\\') {
|
||||
res.text[i] = '/';
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return copy_string(a, res);
|
||||
}
|
||||
|
||||
struct Path {
|
||||
String basename;
|
||||
String name;
|
||||
String ext;
|
||||
};
|
||||
|
||||
// NOTE(Jeroen): Naively turns a Path into a string.
|
||||
gb_internal String path_to_string(gbAllocator a, Path path) {
|
||||
if (path.basename.len + path.name.len + path.ext.len == 0) {
|
||||
return make_string(nullptr, 0);
|
||||
}
|
||||
|
||||
isize len = path.basename.len + 1 + path.name.len + 1;
|
||||
if (path.ext.len > 0) {
|
||||
len += path.ext.len + 1;
|
||||
}
|
||||
|
||||
u8 *str = gb_alloc_array(a, u8, len);
|
||||
|
||||
isize i = 0;
|
||||
gb_memmove(str+i, path.basename.text, path.basename.len); i += path.basename.len;
|
||||
|
||||
gb_memmove(str+i, "/", 1); i += 1;
|
||||
|
||||
gb_memmove(str+i, path.name.text, path.name.len); i += path.name.len;
|
||||
if (path.ext.len > 0) {
|
||||
gb_memmove(str+i, ".", 1); i += 1;
|
||||
gb_memmove(str+i, path.ext.text, path.ext.len); i += path.ext.len;
|
||||
}
|
||||
str[i] = 0;
|
||||
|
||||
String res = make_string(str, i);
|
||||
res = string_trim_whitespace(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
// NOTE(Jeroen): Naively turns a Path into a string, then normalizes it using `path_to_full_path`.
|
||||
gb_internal String path_to_full_path(gbAllocator a, Path path) {
|
||||
String temp = path_to_string(heap_allocator(), path);
|
||||
defer (gb_free(heap_allocator(), temp.text));
|
||||
|
||||
return path_to_full_path(a, temp);
|
||||
}
|
||||
|
||||
// NOTE(Jeroen): Takes a path like "odin" or "W:\Odin", turns it into a full path,
|
||||
// and then breaks it into its components to make a Path.
|
||||
gb_internal Path path_from_string(gbAllocator a, String const &path) {
|
||||
Path res = {};
|
||||
|
||||
if (path.len == 0) return res;
|
||||
|
||||
String fullpath = path_to_full_path(a, path);
|
||||
defer (gb_free(heap_allocator(), fullpath.text));
|
||||
|
||||
res.basename = directory_from_path(fullpath);
|
||||
res.basename = copy_string(a, res.basename);
|
||||
|
||||
if (path_is_directory(fullpath)) {
|
||||
// It's a directory. We don't need to tinker with the name and extension.
|
||||
// It could have a superfluous trailing `/`. Remove it if so.
|
||||
if (res.basename.len > 0 && res.basename.text[res.basename.len - 1] == '/') {
|
||||
res.basename.len--;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// Note(Dragos): Is the copy_string required if it's a substring?
|
||||
isize name_start = (res.basename.len > 0) ? res.basename.len + 1 : res.basename.len;
|
||||
res.name = substring(fullpath, name_start, fullpath.len);
|
||||
res.name = remove_extension_from_path(res.name);
|
||||
res.name = copy_string(a, res.name);
|
||||
|
||||
res.ext = path_extension(fullpath, false); // false says not to include the dot.
|
||||
res.ext = copy_string(a, res.ext);
|
||||
return res;
|
||||
}
|
||||
|
||||
// NOTE(Jeroen): Takes a path String and returns the last path element.
|
||||
gb_internal String last_path_element(String const &path) {
|
||||
isize count = 0;
|
||||
u8 * start = (u8 *)(&path.text[path.len - 1]);
|
||||
for (isize length = path.len; length > 0 && path.text[length - 1] != '/'; length--) {
|
||||
count++;
|
||||
start--;
|
||||
}
|
||||
if (count > 0) {
|
||||
start++; // Advance past the `/` and return the substring.
|
||||
String res = make_string(start, count);
|
||||
return res;
|
||||
}
|
||||
// Must be a root path like `/` or `C:/`, return empty String.
|
||||
return STR_LIT("");
|
||||
}
|
||||
|
||||
gb_internal bool path_is_directory(Path path) {
|
||||
String path_string = path_to_full_path(heap_allocator(), path);
|
||||
defer (gb_free(heap_allocator(), path_string.text));
|
||||
|
||||
return path_is_directory(path_string);
|
||||
}
|
||||
|
||||
struct FileInfo {
|
||||
String name;
|
||||
String fullpath;
|
||||
i64 size;
|
||||
bool is_dir;
|
||||
};
|
||||
|
||||
enum ReadDirectoryError {
|
||||
ReadDirectory_None,
|
||||
|
||||
ReadDirectory_InvalidPath,
|
||||
ReadDirectory_NotExists,
|
||||
ReadDirectory_Permission,
|
||||
ReadDirectory_NotDir,
|
||||
ReadDirectory_Empty,
|
||||
ReadDirectory_Unknown,
|
||||
|
||||
ReadDirectory_COUNT,
|
||||
};
|
||||
|
||||
gb_internal i64 get_file_size(String path) {
|
||||
char *c_str = alloc_cstring(heap_allocator(), path);
|
||||
defer (gb_free(heap_allocator(), c_str));
|
||||
|
||||
gbFile f = {};
|
||||
gbFileError err = gb_file_open(&f, c_str);
|
||||
defer (gb_file_close(&f));
|
||||
if (err != gbFileError_None) {
|
||||
return -1;
|
||||
}
|
||||
return gb_file_size(&f);
|
||||
}
|
||||
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
|
||||
GB_ASSERT(fi != nullptr);
|
||||
|
||||
|
||||
while (path.len > 0) {
|
||||
Rune end = path[path.len-1];
|
||||
if (end == '/') {
|
||||
path.len -= 1;
|
||||
} else if (end == '\\') {
|
||||
path.len -= 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (path.len == 0) {
|
||||
return ReadDirectory_InvalidPath;
|
||||
}
|
||||
{
|
||||
char *c_str = alloc_cstring(temporary_allocator(), path);
|
||||
gbFile f = {};
|
||||
gbFileError file_err = gb_file_open(&f, c_str);
|
||||
defer (gb_file_close(&f));
|
||||
|
||||
switch (file_err) {
|
||||
case gbFileError_Invalid: return ReadDirectory_InvalidPath;
|
||||
case gbFileError_NotExists: return ReadDirectory_NotExists;
|
||||
// case gbFileError_Permission: return ReadDirectory_Permission;
|
||||
}
|
||||
}
|
||||
|
||||
if (!path_is_directory(path)) {
|
||||
return ReadDirectory_NotDir;
|
||||
}
|
||||
|
||||
|
||||
gbAllocator a = heap_allocator();
|
||||
char *new_path = gb_alloc_array(a, char, path.len+3);
|
||||
defer (gb_free(a, new_path));
|
||||
|
||||
gb_memmove(new_path, path.text, path.len);
|
||||
gb_memmove(new_path+path.len, "/*", 2);
|
||||
new_path[path.len+2] = 0;
|
||||
|
||||
String np = make_string(cast(u8 *)new_path, path.len+2);
|
||||
String16 wstr = string_to_string16(a, np);
|
||||
defer (gb_free(a, wstr.text));
|
||||
|
||||
WIN32_FIND_DATAW file_data = {};
|
||||
HANDLE find_file = FindFirstFileW(wstr.text, &file_data);
|
||||
if (find_file == INVALID_HANDLE_VALUE) {
|
||||
return ReadDirectory_Unknown;
|
||||
}
|
||||
defer (FindClose(find_file));
|
||||
|
||||
array_init(fi, a, 0, 100);
|
||||
|
||||
do {
|
||||
wchar_t *filename_w = file_data.cFileName;
|
||||
u64 size = cast(u64)file_data.nFileSizeLow;
|
||||
size |= (cast(u64)file_data.nFileSizeHigh) << 32;
|
||||
String name = string16_to_string(a, make_string16_c(filename_w));
|
||||
if (name == "." || name == "..") {
|
||||
gb_free(a, name.text);
|
||||
continue;
|
||||
}
|
||||
|
||||
String filepath = {};
|
||||
filepath.len = path.len+1+name.len;
|
||||
filepath.text = gb_alloc_array(a, u8, filepath.len+1);
|
||||
defer (gb_free(a, filepath.text));
|
||||
gb_memmove(filepath.text, path.text, path.len);
|
||||
gb_memmove(filepath.text+path.len, "/", 1);
|
||||
gb_memmove(filepath.text+path.len+1, name.text, name.len);
|
||||
|
||||
FileInfo info = {};
|
||||
info.name = name;
|
||||
info.fullpath = path_to_full_path(a, filepath);
|
||||
info.size = cast(i64)size;
|
||||
info.is_dir = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||
array_add(fi, info);
|
||||
} while (FindNextFileW(find_file, &file_data));
|
||||
|
||||
if (fi->count == 0) {
|
||||
return ReadDirectory_Empty;
|
||||
}
|
||||
|
||||
return ReadDirectory_None;
|
||||
}
|
||||
#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD) || defined(GB_SYSTEM_HAIKU)
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
gb_internal ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
|
||||
GB_ASSERT(fi != nullptr);
|
||||
|
||||
gbAllocator a = heap_allocator();
|
||||
|
||||
char *c_path = alloc_cstring(a, path);
|
||||
defer (gb_free(a, c_path));
|
||||
|
||||
DIR *dir = opendir(c_path);
|
||||
if (!dir) {
|
||||
switch (errno) {
|
||||
case ENOENT:
|
||||
return ReadDirectory_NotExists;
|
||||
case EACCES:
|
||||
return ReadDirectory_Permission;
|
||||
case ENOTDIR:
|
||||
return ReadDirectory_NotDir;
|
||||
default:
|
||||
// ENOMEM: out of memory
|
||||
// EMFILE: per-process limit on open fds reached
|
||||
// ENFILE: system-wide limit on total open files reached
|
||||
return ReadDirectory_Unknown;
|
||||
}
|
||||
GB_PANIC("unreachable");
|
||||
}
|
||||
|
||||
array_init(fi, a, 0, 100);
|
||||
|
||||
for (;;) {
|
||||
struct dirent *entry = readdir(dir);
|
||||
if (entry == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
String name = make_string_c(entry->d_name);
|
||||
if (name == "." || name == "..") {
|
||||
continue;
|
||||
}
|
||||
|
||||
String filepath = {};
|
||||
filepath.len = path.len+1+name.len;
|
||||
filepath.text = gb_alloc_array(a, u8, filepath.len+1);
|
||||
defer (gb_free(a, filepath.text));
|
||||
gb_memmove(filepath.text, path.text, path.len);
|
||||
gb_memmove(filepath.text+path.len, "/", 1);
|
||||
gb_memmove(filepath.text+path.len+1, name.text, name.len);
|
||||
filepath.text[filepath.len] = 0;
|
||||
|
||||
|
||||
struct stat dir_stat = {};
|
||||
|
||||
if (stat((char *)filepath.text, &dir_stat)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (S_ISDIR(dir_stat.st_mode)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
i64 size = dir_stat.st_size;
|
||||
|
||||
FileInfo info = {};
|
||||
info.name = name;
|
||||
info.fullpath = path_to_full_path(a, filepath);
|
||||
info.size = size;
|
||||
array_add(fi, info);
|
||||
}
|
||||
|
||||
if (fi->count == 0) {
|
||||
return ReadDirectory_Empty;
|
||||
}
|
||||
|
||||
return ReadDirectory_None;
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
#error Implement read_directory
|
||||
#endif
|
||||
|
||||
#if !defined(GB_SYSTEM_WINDOWS)
|
||||
gb_internal bool write_directory(String path) {
|
||||
char const *pathname = (char *) path.text;
|
||||
|
||||
if (access(pathname, W_OK) < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
gb_internal bool write_directory(String path) {
|
||||
String16 wstr = string_to_string16(heap_allocator(), path);
|
||||
LPCWSTR wdirectory_name = wstr.text;
|
||||
|
||||
HANDLE directory = CreateFileW(wdirectory_name,
|
||||
GENERIC_WRITE,
|
||||
0,
|
||||
NULL,
|
||||
OPEN_EXISTING,
|
||||
FILE_FLAG_BACKUP_SEMANTICS,
|
||||
NULL);
|
||||
|
||||
if (directory == INVALID_HANDLE_VALUE) {
|
||||
DWORD error_code = GetLastError();
|
||||
if (error_code == ERROR_ACCESS_DENIED) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
CloseHandle(directory);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -492,6 +492,8 @@ gb_internal u32 thread_current_id(void) {
|
||||
__asm__("mov %%fs:0x10,%0" : "=r"(thread_id));
|
||||
#elif defined(GB_SYSTEM_LINUX)
|
||||
thread_id = gettid();
|
||||
#elif defined(GB_SYSTEM_HAIKU)
|
||||
thread_id = find_thread(NULL);
|
||||
#else
|
||||
#error Unsupported architecture for thread_current_id()
|
||||
#endif
|
||||
@@ -831,8 +833,178 @@ gb_internal void futex_wait(Futex *f, Footex val) {
|
||||
WaitOnAddress(f, (void *)&val, sizeof(val), INFINITE);
|
||||
} while (f->load() == val);
|
||||
}
|
||||
|
||||
#elif defined(GB_SYSTEM_HAIKU)
|
||||
|
||||
// Futex implementation taken from https://tavianator.com/2023/futex.html
|
||||
|
||||
#include <pthread.h>
|
||||
#include <atomic>
|
||||
|
||||
struct _Spinlock {
|
||||
std::atomic_flag state;
|
||||
|
||||
void init() {
|
||||
state.clear();
|
||||
}
|
||||
|
||||
void lock() {
|
||||
while (state.test_and_set(std::memory_order_acquire)) {
|
||||
#if defined(GB_CPU_X86)
|
||||
_mm_pause();
|
||||
#else
|
||||
(void)0; // spin...
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
state.clear(std::memory_order_release);
|
||||
}
|
||||
};
|
||||
|
||||
struct Futex_Waitq;
|
||||
|
||||
struct Futex_Waiter {
|
||||
_Spinlock lock;
|
||||
pthread_t thread;
|
||||
Futex *futex;
|
||||
Futex_Waitq *waitq;
|
||||
Futex_Waiter *prev, *next;
|
||||
};
|
||||
|
||||
struct Futex_Waitq {
|
||||
_Spinlock lock;
|
||||
Futex_Waiter list;
|
||||
|
||||
void init() {
|
||||
auto head = &list;
|
||||
head->prev = head->next = head;
|
||||
}
|
||||
};
|
||||
|
||||
// FIXME: This approach may scale badly in the future,
|
||||
// possible solution - hash map (leads to deadlocks now).
|
||||
|
||||
Futex_Waitq g_waitq = {
|
||||
.lock = ATOMIC_FLAG_INIT,
|
||||
.list = {
|
||||
.prev = &g_waitq.list,
|
||||
.next = &g_waitq.list,
|
||||
},
|
||||
};
|
||||
|
||||
Futex_Waitq *get_waitq(Futex *f) {
|
||||
// Future hash map method...
|
||||
return &g_waitq;
|
||||
}
|
||||
|
||||
void futex_signal(Futex *f) {
|
||||
auto waitq = get_waitq(f);
|
||||
|
||||
waitq->lock.lock();
|
||||
|
||||
auto head = &waitq->list;
|
||||
for (auto waiter = head->next; waiter != head; waiter = waiter->next) {
|
||||
if (waiter->futex != f) {
|
||||
continue;
|
||||
}
|
||||
waitq->lock.unlock();
|
||||
pthread_kill(waiter->thread, SIGCONT);
|
||||
return;
|
||||
}
|
||||
|
||||
waitq->lock.unlock();
|
||||
}
|
||||
|
||||
void futex_broadcast(Futex *f) {
|
||||
auto waitq = get_waitq(f);
|
||||
|
||||
waitq->lock.lock();
|
||||
|
||||
auto head = &waitq->list;
|
||||
for (auto waiter = head->next; waiter != head; waiter = waiter->next) {
|
||||
if (waiter->futex != f) {
|
||||
continue;
|
||||
}
|
||||
if (waiter->next == head) {
|
||||
waitq->lock.unlock();
|
||||
pthread_kill(waiter->thread, SIGCONT);
|
||||
return;
|
||||
} else {
|
||||
pthread_kill(waiter->thread, SIGCONT);
|
||||
}
|
||||
}
|
||||
|
||||
waitq->lock.unlock();
|
||||
}
|
||||
|
||||
void futex_wait(Futex *f, Footex val) {
|
||||
Futex_Waiter waiter;
|
||||
waiter.thread = pthread_self();
|
||||
waiter.futex = f;
|
||||
|
||||
auto waitq = get_waitq(f);
|
||||
while (waitq->lock.state.test_and_set(std::memory_order_acquire)) {
|
||||
if (f->load(std::memory_order_relaxed) != val) {
|
||||
return;
|
||||
}
|
||||
#if defined(GB_CPU_X86)
|
||||
_mm_pause();
|
||||
#else
|
||||
(void)0; // spin...
|
||||
#endif
|
||||
}
|
||||
|
||||
waiter.waitq = waitq;
|
||||
waiter.lock.init();
|
||||
waiter.lock.lock();
|
||||
|
||||
auto head = &waitq->list;
|
||||
waiter.prev = head->prev;
|
||||
waiter.next = head;
|
||||
waiter.prev->next = &waiter;
|
||||
waiter.next->prev = &waiter;
|
||||
|
||||
waiter.prev->next = &waiter;
|
||||
waiter.next->prev = &waiter;
|
||||
|
||||
sigset_t old_mask, mask;
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIGCONT);
|
||||
pthread_sigmask(SIG_BLOCK, &mask, &old_mask);
|
||||
|
||||
if (f->load(std::memory_order_relaxed) == val) {
|
||||
waiter.lock.unlock();
|
||||
waitq->lock.unlock();
|
||||
|
||||
int sig;
|
||||
sigwait(&mask, &sig);
|
||||
|
||||
waitq->lock.lock();
|
||||
waiter.lock.lock();
|
||||
|
||||
while (waitq != waiter.waitq) {
|
||||
auto req = waiter.waitq;
|
||||
waiter.lock.unlock();
|
||||
waitq->lock.unlock();
|
||||
waitq = req;
|
||||
waitq->lock.lock();
|
||||
waiter.lock.lock();
|
||||
}
|
||||
}
|
||||
|
||||
waiter.prev->next = waiter.next;
|
||||
waiter.next->prev = waiter.prev;
|
||||
|
||||
pthread_sigmask(SIG_SETMASK, &old_mask, NULL);
|
||||
|
||||
waiter.lock.unlock();
|
||||
waitq->lock.unlock();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -825,6 +825,7 @@ gb_internal bool cg_generate_code(Checker *c, LinkerData *linker_data) {
|
||||
case TargetOs_essence:
|
||||
case TargetOs_freebsd:
|
||||
case TargetOs_openbsd:
|
||||
case TargetOs_haiku:
|
||||
debug_format = TB_DEBUGFMT_DWARF;
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user