Merge pull request #4012 from laytan/posix

core:sys/posix and core:os/os2 based on it (for darwin, netbsd, freebsd and openbsd)
This commit is contained in:
gingerBill
2024-08-14 15:10:31 +01:00
committed by GitHub
114 changed files with 14316 additions and 401 deletions

View File

@@ -47,8 +47,8 @@ foreign libc {
clogf :: proc(z: complex_float) -> complex_float ---
// 7.3.8 Power and absolute-value functions
cabs :: proc(z: complex_double) -> complex_double ---
cabsf :: proc(z: complex_float) -> complex_float ---
cabs :: proc(z: complex_double) -> double ---
cabsf :: proc(z: complex_float) -> float ---
cpow :: proc(x, y: complex_double) -> complex_double ---
cpowf :: proc(x, y: complex_float) -> complex_float ---
csqrt :: proc(z: complex_double) -> complex_double ---

View File

@@ -102,6 +102,6 @@ when ODIN_OS == .Haiku {
// 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
// it actually is.
errno :: #force_inline proc() -> ^int {
errno :: #force_inline proc "contextless" () -> ^int {
return _get_errno()
}

View File

@@ -32,24 +32,21 @@ when ODIN_OS == .Windows {
// the RDX register will contain zero and correctly set the flag to disable
// stack unwinding.
@(link_name="_setjmp")
setjmp :: proc(env: ^jmp_buf, hack: rawptr = nil) -> int ---
setjmp :: proc(env: ^jmp_buf, hack: rawptr = nil) -> int ---
}
} else {
@(default_calling_convention="c")
foreign libc {
// 7.13.1 Save calling environment
//
// NOTE(dweiler): C11 requires setjmp be a macro, which means it won't
// necessarily export a symbol named setjmp but rather _setjmp in the case
// of musl, glibc, BSD libc, and msvcrt.
@(link_name="_setjmp")
setjmp :: proc(env: ^jmp_buf) -> int ---
@(link_name=LSETJMP)
setjmp :: proc(env: ^jmp_buf) -> int ---
}
}
@(default_calling_convention="c")
foreign libc {
// 7.13.2 Restore calling environment
@(link_name=LLONGJMP)
longjmp :: proc(env: ^jmp_buf, val: int) -> ! ---
}
@@ -64,3 +61,11 @@ foreign libc {
// The choice of 4096 bytes for storage of this type is more than enough on all
// relevant platforms.
jmp_buf :: struct #align(16) { _: [4096]char, }
when ODIN_OS == .NetBSD {
@(private) LSETJMP :: "__setjmp14"
@(private) LLONGJMP :: "__longjmp14"
} else {
@(private) LSETJMP :: "setjmp"
@(private) LLONGJMP :: "longjmp"
}

View File

@@ -17,6 +17,12 @@ when ODIN_OS == .Windows {
FILE :: struct {}
Whence :: enum int {
SET = SEEK_SET,
CUR = SEEK_CUR,
END = SEEK_END,
}
// MSVCRT compatible.
when ODIN_OS == .Windows {
_IOFBF :: 0x0000
@@ -101,6 +107,8 @@ when ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD {
SEEK_CUR :: 1
SEEK_END :: 2
TMP_MAX :: 308915776
foreign libc {
__sF: [3]FILE
}
@@ -128,6 +136,8 @@ when ODIN_OS == .FreeBSD {
SEEK_CUR :: 1
SEEK_END :: 2
TMP_MAX :: 308915776
foreign libc {
@(link_name="__stderrp") stderr: ^FILE
@(link_name="__stdinp") stdin: ^FILE
@@ -195,10 +205,21 @@ when ODIN_OS == .Haiku {
}
}
when ODIN_OS == .NetBSD {
@(private) LRENAME :: "__posix_rename"
@(private) LFGETPOS :: "__fgetpos50"
@(private) LFSETPOS :: "__fsetpos50"
} else {
@(private) LRENAME :: "rename"
@(private) LFGETPOS :: "fgetpos"
@(private) LFSETPOS :: "fsetpos"
}
@(default_calling_convention="c")
foreign libc {
// 7.21.4 Operations on files
remove :: proc(filename: cstring) -> int ---
@(link_name=LRENAME)
rename :: proc(old, new: cstring) -> int ---
tmpfile :: proc() -> ^FILE ---
tmpnam :: proc(s: [^]char) -> [^]char ---
@@ -240,8 +261,10 @@ foreign libc {
fwrite :: proc(ptr: rawptr, size: size_t, nmemb: size_t, stream: ^FILE) -> size_t ---
// 7.21.9 File positioning functions
@(link_name=LFGETPOS)
fgetpos :: proc(stream: ^FILE, pos: ^fpos_t) -> int ---
fseek :: proc(stream: ^FILE, offset: long, whence: int) -> int ---
fseek :: proc(stream: ^FILE, offset: long, whence: Whence) -> int ---
@(link_name=LFSETPOS)
fsetpos :: proc(stream: ^FILE, pos: ^fpos_t) -> int ---
ftell :: proc(stream: ^FILE) -> long ---
rewind :: proc(stream: ^FILE) ---
@@ -288,11 +311,11 @@ to_stream :: proc(file: ^FILE) -> io.Stream {
return 0, unknown_or_eof(file)
}
if fseek(file, long(offset), SEEK_SET) != 0 {
if fseek(file, long(offset), .SET) != 0 {
return 0, unknown_or_eof(file)
}
defer fseek(file, long(curr), SEEK_SET)
defer fseek(file, long(curr), .SET)
n = i64(fread(raw_data(p), size_of(byte), len(p), file))
if n == 0 { err = unknown_or_eof(file) }
@@ -307,17 +330,21 @@ to_stream :: proc(file: ^FILE) -> io.Stream {
return 0, unknown_or_eof(file)
}
if fseek(file, long(offset), SEEK_SET) != 0 {
if fseek(file, long(offset), .SET) != 0 {
return 0, unknown_or_eof(file)
}
defer fseek(file, long(curr), SEEK_SET)
defer fseek(file, long(curr), .SET)
n = i64(fwrite(raw_data(p), size_of(byte), len(p), file))
if n == 0 { err = unknown_or_eof(file) }
case .Seek:
if fseek(file, long(offset), int(whence)) != 0 {
#assert(int(Whence.SET) == int(io.Seek_From.Start))
#assert(int(Whence.CUR) == int(io.Seek_From.Current))
#assert(int(Whence.END) == int(io.Seek_From.End))
if fseek(file, long(offset), Whence(whence)) != 0 {
return 0, unknown_or_eof(file)
}
@@ -326,9 +353,9 @@ to_stream :: proc(file: ^FILE) -> io.Stream {
if curr == -1 {
return 0, unknown_or_eof(file)
}
defer fseek(file, curr, SEEK_SET)
defer fseek(file, curr, .SET)
if fseek(file, 0, SEEK_END) != 0 {
if fseek(file, 0, .END) != 0 {
return 0, unknown_or_eof(file)
}

View File

@@ -40,10 +40,9 @@ when ODIN_OS == .Linux {
}
when ODIN_OS == .Darwin {
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD {
RAND_MAX :: 0x7fffffff
// GLIBC and MUSL only
@(private="file")
@(default_calling_convention="c")
foreign libc {
@@ -55,6 +54,20 @@ when ODIN_OS == .Darwin {
}
}
when ODIN_OS == .NetBSD {
RAND_MAX :: 0x7fffffff
@(private="file")
@(default_calling_convention="c")
foreign libc {
__mb_cur_max: size_t
}
MB_CUR_MAX :: #force_inline proc() -> size_t {
return __mb_cur_max
}
}
// C does not declare what these values should be, as an implementation is free
// to use any two distinct values it wants to indicate success or failure.
// However, nobody actually does and everyone appears to have agreed upon these
@@ -99,7 +112,7 @@ foreign libc {
at_quick_exit :: proc(func: proc "c" ()) -> int ---
exit :: proc(status: int) -> ! ---
_Exit :: proc(status: int) -> ! ---
getenv :: proc(name: cstring) -> [^]char ---
getenv :: proc(name: cstring) -> cstring ---
quick_exit :: proc(status: int) -> ! ---
system :: proc(cmd: cstring) -> int ---
@@ -150,4 +163,4 @@ aligned_free :: #force_inline proc "c" (ptr: rawptr) {
} else {
free(ptr)
}
}
}

View File

@@ -40,7 +40,7 @@ foreign libc {
strtok :: proc(s1: [^]char, s2: cstring) -> [^]char ---
// 7.24.6 Miscellaneous functions
strerror :: proc(errnum: int) -> [^]char ---
strerror :: proc(errnum: int) -> cstring ---
strlen :: proc(s: cstring) -> size_t ---
}
memset :: proc "c" (s: rawptr, c: int, n: size_t) -> rawptr {

View File

@@ -50,30 +50,56 @@ when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Darwin || ODIN_OS =
foreign libc {
// 7.27.2 Time manipulation functions
clock :: proc() -> clock_t ---
@(link_name=LDIFFTIME)
difftime :: proc(time1, time2: time_t) -> double ---
@(link_name=LMKTIME)
mktime :: proc(timeptr: ^tm) -> time_t ---
@(link_name=LTIME)
time :: proc(timer: ^time_t) -> time_t ---
timespec_get :: proc(ts: ^timespec, base: int) -> int ---
// 7.27.3 Time conversion functions
asctime :: proc(timeptr: ^tm) -> [^]char ---
@(link_name=LCTIME)
ctime :: proc(timer: ^time_t) -> [^]char ---
@(link_name=LGMTIME)
gmtime :: proc(timer: ^time_t) -> ^tm ---
@(link_name=LLOCALTIME)
localtime :: proc(timer: ^time_t) -> ^tm ---
strftime :: proc(s: [^]char, maxsize: size_t, format: cstring, timeptr: ^tm) -> size_t ---
}
when ODIN_OS == .NetBSD {
@(private) LDIFFTIME :: "__difftime50"
@(private) LMKTIME :: "__mktime50"
@(private) LTIME :: "__time50"
@(private) LCTIME :: "__ctime50"
@(private) LGMTIME :: "__gmtime50"
@(private) LLOCALTIME :: "__localtime50"
} else {
@(private) LDIFFTIME :: "difftime"
@(private) LMKTIME :: "mktime"
@(private) LTIME :: "ltime"
@(private) LCTIME :: "ctime"
@(private) LGMTIME :: "gmtime"
@(private) LLOCALTIME :: "localtime"
}
when ODIN_OS == .OpenBSD {
CLOCKS_PER_SEC :: 100
} else {
CLOCKS_PER_SEC :: 1000000
}
TIME_UTC :: 1
TIME_UTC :: 1
time_t :: distinct i64
time_t :: distinct i64
clock_t :: long
when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD {
clock_t :: distinct int32_t
} else {
clock_t :: distinct long
}
timespec :: struct {
tv_sec: time_t,

View File

@@ -1,165 +0,0 @@
//+build darwin
//+private
package mem_virtual
foreign import libc "system:System.framework"
import "core:c"
PROT_NONE :: 0x0 /* [MC2] no permissions */
PROT_READ :: 0x1 /* [MC2] pages can be read */
PROT_WRITE :: 0x2 /* [MC2] pages can be written */
PROT_EXEC :: 0x4 /* [MC2] pages can be executed */
// Sharing options
MAP_SHARED :: 0x1 /* [MF|SHM] share changes */
MAP_PRIVATE :: 0x2 /* [MF|SHM] changes are private */
// Other flags
MAP_FIXED :: 0x0010 /* [MF|SHM] interpret addr exactly */
MAP_RENAME :: 0x0020 /* Sun: rename private pages to file */
MAP_NORESERVE :: 0x0040 /* Sun: don't reserve needed swap area */
MAP_RESERVED0080 :: 0x0080 /* previously unimplemented MAP_INHERIT */
MAP_NOEXTEND :: 0x0100 /* for MAP_FILE, don't change file size */
MAP_HASSEMAPHORE :: 0x0200 /* region may contain semaphores */
MAP_NOCACHE :: 0x0400 /* don't cache pages for this mapping */
MAP_JIT :: 0x0800 /* Allocate a region that will be used for JIT purposes */
// Mapping type
MAP_FILE :: 0x0000 /* map from file (default) */
MAP_ANONYMOUS :: 0x1000 /* allocated from memory, swap space */
/*
* The MAP_RESILIENT_* flags can be used when the caller wants to map some
* possibly unreliable memory and be able to access it safely, possibly
* getting the wrong contents rather than raising any exception.
* For safety reasons, such mappings have to be read-only (PROT_READ access
* only).
*
* MAP_RESILIENT_CODESIGN:
* accessing this mapping will not generate code-signing violations,
* even if the contents are tainted.
* MAP_RESILIENT_MEDIA:
* accessing this mapping will not generate an exception if the contents
* are not available (unreachable removable or remote media, access beyond
* end-of-file, ...). Missing contents will be replaced with zeroes.
*/
MAP_RESILIENT_CODESIGN :: 0x2000 /* no code-signing failures */
MAP_RESILIENT_MEDIA :: 0x4000 /* no backing-store failures */
MAP_32BIT :: 0x8000 /* Return virtual addresses <4G only */
// Flags used to support translated processes.
MAP_TRANSLATED_ALLOW_EXECUTE :: 0x20000 /* allow execute in translated processes */
MAP_UNIX03 :: 0x40000 /* UNIX03 compliance */
// Process memory locking
MCL_CURRENT :: 0x0001 /* [ML] Lock only current memory */
MCL_FUTURE :: 0x0002 /* [ML] Lock all future memory as well */
MADV_NORMAL :: 0 /* [MC1] no further special treatment */
MADV_RANDOM :: 1 /* [MC1] expect random page refs */
MADV_SEQUENTIAL :: 2 /* [MC1] expect sequential page refs */
MADV_WILLNEED :: 3 /* [MC1] will need these pages */
MADV_DONTNEED :: 4 /* [MC1] dont need these pages */
MADV_FREE :: 5 /* pages unneeded, discard contents */
MADV_ZERO_WIRED_PAGES :: 6 /* zero the wired pages that have not been unwired before the entry is deleted */
MADV_FREE_REUSABLE :: 7 /* pages can be reused (by anyone) */
MADV_FREE_REUSE :: 8 /* caller wants to reuse those pages */
MADV_CAN_REUSE :: 9
MADV_PAGEOUT :: 10 /* page out now (internal only) */
// msync() flags
MS_ASYNC :: 0x0001 /* [MF|SIO] return immediately */
MS_INVALIDATE :: 0x0002 /* [MF|SIO] invalidate all cached data */
MS_SYNC :: 0x0010 /* [MF|SIO] msync synchronously */
MS_KILLPAGES :: 0x0004 /* invalidate pages, leave mapped */
MS_DEACTIVATE :: 0x0008 /* deactivate pages, leave mapped */
// Return bits from mincore
MINCORE_INCORE :: 0x1 /* Page is incore */
MINCORE_REFERENCED :: 0x2 /* Page has been referenced by us */
MINCORE_MODIFIED :: 0x4 /* Page has been modified by us */
MINCORE_REFERENCED_OTHER :: 0x8 /* Page has been referenced */
MINCORE_MODIFIED_OTHER :: 0x10 /* Page has been modified */
MINCORE_PAGED_OUT :: 0x20 /* Page has been paged out */
MINCORE_COPIED :: 0x40 /* Page has been copied */
MINCORE_ANONYMOUS :: 0x80 /* Page belongs to an anonymous object */
// Allocation failure result
MAP_FAILED : rawptr = rawptr(~uintptr(0))
foreign libc {
@(link_name="mlockall") _mlockall :: proc(flags: c.int) -> c.int ---
@(link_name="munlockall") _munlockall :: proc() -> c.int ---
@(link_name="mlock") _mlock :: proc(addr: rawptr, len: c.size_t) -> c.int ---
@(link_name="mmap") _mmap :: proc(addr: rawptr, len: c.size_t, prot: c.int, flags: c.int, fd: c.int, offset: int) -> rawptr ---
@(link_name="mprotect") _mprotect :: proc(addr: rawptr, len: c.size_t, prot: c.int) -> c.int ---
@(link_name="msync") _msync :: proc(addr: rawptr, len: c.size_t) -> c.int ---
@(link_name="munlock") _munlock :: proc(addr: rawptr, len: c.size_t) -> c.int ---
@(link_name="munmap") _munmap :: proc(addr: rawptr, len: c.size_t) -> c.int ---
@(link_name="shm_open") _shm_open :: proc(name: cstring, oflag: c.int, #c_vararg args: ..any) -> c.int ---
@(link_name="shm_unlink") _shm_unlink :: proc(name: cstring) -> c.int ---
@(link_name="posix_madvise") _posix_madvise :: proc(addr: rawptr, len: c.size_t, advice: c.int) -> c.int ---
@(link_name="madvise") _madvise :: proc(addr: rawptr, len: c.size_t, advice: c.int) -> c.int ---
@(link_name="mincore") _mincore :: proc(addr: rawptr, len: c.size_t, vec: cstring) -> c.int ---
@(link_name="minherit") _minherit :: proc(addr: rawptr, len: c.size_t, inherit: c.int) -> c.int ---
}
_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
result := _mmap(nil, size, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)
if result == MAP_FAILED {
return nil, .Out_Of_Memory
}
return ([^]byte)(uintptr(result))[:size], nil
}
_commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
result := _mprotect(data, size, PROT_READ|PROT_WRITE)
if result != 0 {
return .Out_Of_Memory
}
return nil
}
_decommit :: proc "contextless" (data: rawptr, size: uint) {
_mprotect(data, size, PROT_NONE)
_madvise(data, size, MADV_FREE)
}
_release :: proc "contextless" (data: rawptr, size: uint) {
_munmap(data, size)
}
_protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool {
pflags: c.int
pflags = PROT_NONE
if .Read in flags { pflags |= PROT_READ }
if .Write in flags { pflags |= PROT_WRITE }
if .Execute in flags { pflags |= PROT_EXEC }
err := _mprotect(data, size, pflags)
return err == 0
}
_platform_memory_init :: proc() {
DEFAULT_PAGE_SIZE = 4096
// is power of two
assert(DEFAULT_PAGE_SIZE != 0 && (DEFAULT_PAGE_SIZE & (DEFAULT_PAGE_SIZE-1)) == 0)
}
_map_file :: proc "contextless" (fd: uintptr, size: i64, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) {
prot, mflags: c.int
if .Read in flags {
prot |= PROT_READ
}
if .Write in flags {
prot |= PROT_WRITE
}
mflags |= MAP_SHARED
addr := _mmap(nil, c.size_t(size), prot, mflags, i32(fd), 0)
if addr == nil {
return nil, .Map_Failure
}
return ([^]byte)(addr)[:size], nil
}

View File

@@ -1,5 +1,8 @@
//+private
//+build !darwin
//+build !freebsd
//+build !openbsd
//+build !netbsd
//+build !linux
//+build !windows
package mem_virtual

View File

@@ -0,0 +1,69 @@
//+build darwin, netbsd, freebsd, openbsd
//+private
package mem_virtual
import "core:sys/posix"
// Define non-posix needed flags:
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD {
MAP_ANONYMOUS :: 0x1000 /* allocated from memory, swap space */
MADV_FREE :: 5 /* pages unneeded, discard contents */
} else when ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD {
MAP_ANONYMOUS :: 0x1000
MADV_FREE :: 6
}
_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
flags := posix.Map_Flags{ .PRIVATE } + transmute(posix.Map_Flags)i32(MAP_ANONYMOUS)
result := posix.mmap(nil, size, {}, flags)
if result == posix.MAP_FAILED {
return nil, .Out_Of_Memory
}
return ([^]byte)(uintptr(result))[:size], nil
}
_commit :: proc "contextless" (data: rawptr, size: uint) -> Allocator_Error {
if posix.mprotect(data, size, { .READ, .WRITE }) != .OK {
return .Out_Of_Memory
}
return nil
}
_decommit :: proc "contextless" (data: rawptr, size: uint) {
posix.mprotect(data, size, {})
posix.posix_madvise(data, size, transmute(posix.MAdvice)i32(MADV_FREE))
}
_release :: proc "contextless" (data: rawptr, size: uint) {
posix.munmap(data, size)
}
_protect :: proc "contextless" (data: rawptr, size: uint, flags: Protect_Flags) -> bool {
#assert(i32(posix.Prot_Flag_Bits.READ) == i32(Protect_Flag.Read))
#assert(i32(posix.Prot_Flag_Bits.WRITE) == i32(Protect_Flag.Write))
#assert(i32(posix.Prot_Flag_Bits.EXEC) == i32(Protect_Flag.Execute))
return posix.mprotect(data, size, transmute(posix.Prot_Flags)flags) == .OK
}
_platform_memory_init :: proc() {
DEFAULT_PAGE_SIZE = posix.PAGE_SIZE
// is power of two
assert(DEFAULT_PAGE_SIZE != 0 && (DEFAULT_PAGE_SIZE & (DEFAULT_PAGE_SIZE-1)) == 0)
}
_map_file :: proc "contextless" (fd: uintptr, size: i64, flags: Map_File_Flags) -> (data: []byte, error: Map_File_Error) {
#assert(i32(posix.Prot_Flag_Bits.READ) == i32(Map_File_Flag.Read))
#assert(i32(posix.Prot_Flag_Bits.WRITE) == i32(Map_File_Flag.Write))
addr := posix.mmap(nil, uint(size), transmute(posix.Prot_Flags)flags, { .SHARED }, posix.FD(fd))
if addr == posix.MAP_FAILED || addr == nil {
return nil, .Map_Failure
}
return ([^]byte)(addr)[:size], nil
}

View File

@@ -61,4 +61,3 @@ TEMP_ALLOCATOR_GUARD :: #force_inline proc(loc := #caller_location) -> (runtime.
global_default_temp_allocator_index = (global_default_temp_allocator_index+1)%MAX_TEMP_ARENA_COUNT
return tmp, loc
}

View File

@@ -0,0 +1,92 @@
//+private
//+build darwin, netbsd, freebsd, openbsd
package os2
import "core:sys/posix"
Read_Directory_Iterator_Impl :: struct {
dir: posix.DIR,
idx: int,
fullpath: [dynamic]byte,
}
@(require_results)
_read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
fimpl := (^File_Impl)(it.f.impl)
index = it.impl.idx
it.impl.idx += 1
for {
entry := posix.readdir(it.impl.dir)
if entry == nil {
// NOTE(laytan): would be good to have an `error` field on the `Read_Directory_Iterator`
// There isn't a way to now know if it failed or if we are at the end.
return
}
cname := cstring(raw_data(entry.d_name[:]))
if cname == "." || cname == ".." {
continue
}
sname := string(cname)
stat: posix.stat_t
if posix.fstatat(posix.dirfd(it.impl.dir), cname, &stat, { .SYMLINK_NOFOLLOW }) != .OK {
// NOTE(laytan): would be good to have an `error` field on the `Read_Directory_Iterator`
// There isn't a way to now know if it failed or if we are at the end.
return
}
n := len(fimpl.name)+1
non_zero_resize(&it.impl.fullpath, n+len(sname))
n += copy(it.impl.fullpath[n:], sname)
fi = internal_stat(stat, string(it.impl.fullpath[:]))
ok = true
return
}
}
@(require_results)
_read_directory_iterator_create :: proc(f: ^File) -> (iter: Read_Directory_Iterator, err: Error) {
if f == nil || f.impl == nil {
err = .Invalid_File
return
}
impl := (^File_Impl)(f.impl)
iter.f = f
iter.impl.idx = 0
iter.impl.fullpath.allocator = file_allocator()
append(&iter.impl.fullpath, impl.name)
append(&iter.impl.fullpath, "/")
defer if err != nil { delete(iter.impl.fullpath) }
// `fdopendir` consumes the file descriptor so we need to `dup` it.
dupfd := posix.dup(impl.fd)
if dupfd == -1 {
err = _get_platform_error()
return
}
defer if err != nil { posix.close(dupfd) }
iter.impl.dir = posix.fdopendir(dupfd)
if iter.impl.dir == nil {
err = _get_platform_error()
return
}
return
}
_read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
if it == nil || it.impl.dir == nil {
return
}
posix.closedir(it.impl.dir)
delete(it.impl.fullpath)
}

View File

@@ -138,4 +138,4 @@ _read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
}
file_info_delete(it.impl.prev_fi, file_allocator())
win32.FindClose(it.impl.find_handle)
}
}

View File

@@ -0,0 +1,77 @@
//+private
//+build darwin, netbsd, freebsd, openbsd
package os2
import "base:runtime"
import "core:strings"
import "core:sys/posix"
_lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
if key == "" {
return
}
TEMP_ALLOCATOR_GUARD()
ckey := strings.clone_to_cstring(key, temp_allocator())
cval := posix.getenv(ckey)
if cval == nil {
return
}
found = true
value = strings.clone(string(cval), allocator) // NOTE(laytan): what if allocation fails?
return
}
_set_env :: proc(key, value: string) -> (ok: bool) {
TEMP_ALLOCATOR_GUARD()
ckey := strings.clone_to_cstring(key, temp_allocator())
cval := strings.clone_to_cstring(key, temp_allocator())
ok = posix.setenv(ckey, cval, true) == .OK
return
}
_unset_env :: proc(key: string) -> (ok: bool) {
TEMP_ALLOCATOR_GUARD()
ckey := strings.clone_to_cstring(key, temp_allocator())
ok = posix.unsetenv(ckey) == .OK
return
}
// NOTE(laytan): clearing the env is weird, why would you ever do that?
_clear_env :: proc() {
for i, entry := 0, posix.environ[0]; entry != nil; i, entry = i+1, posix.environ[i] {
key := strings.truncate_to_byte(string(entry), '=')
_unset_env(key)
}
}
_environ :: proc(allocator: runtime.Allocator) -> (environ: []string) {
n := 0
for entry := posix.environ[0]; entry != nil; n, entry = n+1, posix.environ[n] {}
err: runtime.Allocator_Error
if environ, err = make([]string, n, allocator); err != nil {
// NOTE(laytan): is the environment empty or did allocation fail, how does the user know?
return
}
for i, entry := 0, posix.environ[0]; entry != nil; i, entry = i+1, posix.environ[i] {
if environ[i], err = strings.clone(string(entry), allocator); err != nil {
// NOTE(laytan): is the entire environment returned or did allocation fail, how does the user know?
return
}
}
return
}

View File

@@ -20,8 +20,6 @@ _lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string
return "", true
}
TEMP_ALLOCATOR_GUARD()
b := make([]u16, n+1, temp_allocator())
n = win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b)))

View File

@@ -0,0 +1,30 @@
//+private
//+build darwin, netbsd, freebsd, openbsd
package os2
import "core:sys/posix"
_error_string :: proc(errno: i32) -> string {
return string(posix.strerror(posix.Errno(errno)))
}
_get_platform_error :: proc() -> Error {
#partial switch errno := posix.errno(); errno {
case .EPERM:
return .Permission_Denied
case .EEXIST:
return .Exist
case .ENOENT:
return .Not_Exist
case .ETIMEDOUT:
return .Timeout
case .EPIPE:
return .Broken_Pipe
case .EBADF:
return .Invalid_File
case .ENOMEM:
return .Out_Of_Memory
case:
return Platform_Error(errno)
}
}

View File

@@ -115,7 +115,11 @@ open :: proc(name: string, flags := File_Flags{.Read}, perm := 0o777) -> (^File,
@(require_results)
new_file :: proc(handle: uintptr, name: string) -> ^File {
return _new_file(handle, name) or_else panic("Out of memory")
file, err := _new_file(handle, name)
if err != nil {
panic(error_string(err))
}
return file
}
@(require_results)

454
core/os/os2/file_posix.odin Normal file
View File

@@ -0,0 +1,454 @@
//+private
//+build darwin, netbsd, freebsd, openbsd
package os2
import "base:runtime"
import "core:io"
import "core:c"
import "core:time"
import "core:sys/posix"
// Most implementations will EINVAL at some point when doing big writes.
// 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.
MAX_RW :: 1 << 30
File_Impl :: struct {
file: File,
name: string,
cname: cstring,
fd: posix.FD,
}
@(init)
init_std_files :: proc() {
// NOTE: is this (paths) also the case on non darwin?
stdin = __new_file(posix.STDIN_FILENO)
(^File_Impl)(stdin.impl).name = "/dev/stdin"
(^File_Impl)(stdin.impl).cname = "/dev/stdin"
stdout = __new_file(posix.STDIN_FILENO)
(^File_Impl)(stdout.impl).name = "/dev/stdout"
(^File_Impl)(stdout.impl).cname = "/dev/stdout"
stderr = __new_file(posix.STDIN_FILENO)
(^File_Impl)(stderr.impl).name = "/dev/stderr"
(^File_Impl)(stderr.impl).cname = "/dev/stderr"
}
_open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) {
if name == "" {
err = .Invalid_Path
return
}
sys_flags := posix.O_Flags{.NOCTTY, .CLOEXEC}
if .Write in flags {
if .Read in flags {
sys_flags += {.RDWR}
} else {
sys_flags += {.WRONLY}
}
}
if .Append in flags { sys_flags += {.APPEND} }
if .Create in flags { sys_flags += {.CREAT} }
if .Excl in flags { sys_flags += {.EXCL} }
if .Sync in flags { sys_flags += {.DSYNC} }
if .Trunc in flags { sys_flags += {.TRUNC} }
if .Inheritable in flags { sys_flags -= {.CLOEXEC} }
TEMP_ALLOCATOR_GUARD()
cname := temp_cstring(name)
fd := posix.open(cname, sys_flags, transmute(posix.mode_t)posix._mode_t(perm))
if fd < 0 {
err = _get_platform_error()
return
}
return _new_file(uintptr(fd), name)
}
_new_file :: proc(handle: uintptr, name: string) -> (f: ^File, err: Error) {
if name == "" {
err = .Invalid_Path
return
} else if handle == ~uintptr(0) {
err = .Invalid_File
return
}
crname := _posix_absolute_path(posix.FD(handle), name, file_allocator()) or_return
rname := string(crname)
f = __new_file(posix.FD(handle))
impl := (^File_Impl)(f.impl)
impl.name = rname
impl.cname = crname
return f, nil
}
__new_file :: proc(handle: posix.FD) -> ^File {
impl := new(File_Impl, file_allocator())
impl.file.impl = impl
impl.fd = posix.FD(handle)
impl.file.stream = {
data = impl,
procedure = _file_stream_proc,
}
impl.file.fstat = _fstat
return &impl.file
}
_close :: proc(f: ^File_Impl) -> (err: Error) {
if f == nil { return nil }
if posix.close(f.fd) != .OK {
err = _get_platform_error()
}
delete(f.cname, file_allocator())
free(f, file_allocator())
return
}
_fd :: proc(f: ^File) -> uintptr {
return uintptr(__fd(f))
}
__fd :: proc(f: ^File) -> posix.FD {
if f != nil && f.impl != nil {
return (^File_Impl)(f.impl).fd
}
return -1
}
_name :: proc(f: ^File) -> string {
if f != nil && f.impl != nil {
return (^File_Impl)(f.impl).name
}
return ""
}
_sync :: proc(f: ^File) -> Error {
if posix.fsync(__fd(f)) != .OK {
return _get_platform_error()
}
return nil
}
_truncate :: proc(f: ^File, size: i64) -> Error {
if posix.ftruncate(__fd(f), posix.off_t(size)) != .OK {
return _get_platform_error()
}
return nil
}
_remove :: proc(name: string) -> Error {
TEMP_ALLOCATOR_GUARD()
cname := temp_cstring(name)
if posix.remove(cname) != 0 {
return _get_platform_error()
}
return nil
}
_rename :: proc(old_path, new_path: string) -> Error {
TEMP_ALLOCATOR_GUARD()
cold := temp_cstring(old_path)
cnew := temp_cstring(new_path)
if posix.rename(cold, cnew) != 0 {
return _get_platform_error()
}
return nil
}
_link :: proc(old_name, new_name: string) -> Error {
TEMP_ALLOCATOR_GUARD()
cold := temp_cstring(old_name)
cnew := temp_cstring(new_name)
if posix.link(cold, cnew) != .OK {
return _get_platform_error()
}
return nil
}
_symlink :: proc(old_name, new_name: string) -> Error {
TEMP_ALLOCATOR_GUARD()
cold := temp_cstring(old_name)
cnew := temp_cstring(new_name)
if posix.symlink(cold, cnew) != .OK {
return _get_platform_error()
}
return nil
}
_read_link :: proc(name: string, allocator: runtime.Allocator) -> (s: string, err: Error) {
TEMP_ALLOCATOR_GUARD()
cname := temp_cstring(name)
buf: [dynamic]byte
buf.allocator = allocator
defer if err != nil { delete(buf) }
// Loop this because the file might've grown between lstat() and readlink().
for {
stat: posix.stat_t
if posix.lstat(cname, &stat) != .OK {
err = _get_platform_error()
return
}
bufsiz := int(stat.st_size + 1 if stat.st_size > 0 else posix.PATH_MAX)
if bufsiz == len(buf) {
bufsiz *= 2
}
// Overflow.
if bufsiz <= 0 {
err = Platform_Error(posix.Errno.E2BIG)
return
}
resize(&buf, bufsiz) or_return
size := posix.readlink(cname, raw_data(buf), uint(bufsiz))
if size < 0 {
err = _get_platform_error()
return
}
// File has probably grown between lstat() and readlink().
if size == bufsiz {
continue
}
s = string(buf[:size])
return
}
}
_chdir :: proc(name: string) -> Error {
TEMP_ALLOCATOR_GUARD()
cname := temp_cstring(name)
if posix.chdir(cname) != .OK {
return _get_platform_error()
}
return nil
}
_fchdir :: proc(f: ^File) -> Error {
if posix.fchdir(__fd(f)) != .OK {
return _get_platform_error()
}
return nil
}
_fchmod :: proc(f: ^File, mode: int) -> Error {
if posix.fchmod(__fd(f), transmute(posix.mode_t)posix._mode_t(mode)) != .OK {
return _get_platform_error()
}
return nil
}
_chmod :: proc(name: string, mode: int) -> Error {
TEMP_ALLOCATOR_GUARD()
cname := temp_cstring(name)
if posix.chmod(cname, transmute(posix.mode_t)posix._mode_t(mode)) != .OK {
return _get_platform_error()
}
return nil
}
_fchown :: proc(f: ^File, uid, gid: int) -> Error {
if posix.fchown(__fd(f), posix.uid_t(uid), posix.gid_t(gid)) != .OK {
return _get_platform_error()
}
return nil
}
_chown :: proc(name: string, uid, gid: int) -> Error {
TEMP_ALLOCATOR_GUARD()
cname := temp_cstring(name)
if posix.chown(cname, posix.uid_t(uid), posix.gid_t(gid)) != .OK {
return _get_platform_error()
}
return nil
}
_lchown :: proc(name: string, uid, gid: int) -> Error {
TEMP_ALLOCATOR_GUARD()
cname := temp_cstring(name)
if posix.lchown(cname, posix.uid_t(uid), posix.gid_t(gid)) != .OK {
return _get_platform_error()
}
return nil
}
_chtimes :: proc(name: string, atime, mtime: time.Time) -> Error {
times := [2]posix.timeval{
{
tv_sec = posix.time_t(atime._nsec/1e9), /* seconds */
tv_usec = posix.suseconds_t(atime._nsec%1e9/1000), /* microseconds */
},
{
tv_sec = posix.time_t(mtime._nsec/1e9), /* seconds */
tv_usec = posix.suseconds_t(mtime._nsec%1e9/1000), /* microseconds */
},
}
TEMP_ALLOCATOR_GUARD()
cname := temp_cstring(name)
if posix.utimes(cname, &times) != .OK {
return _get_platform_error()
}
return nil
}
_fchtimes :: proc(f: ^File, atime, mtime: time.Time) -> Error {
times := [2]posix.timespec{
{
tv_sec = posix.time_t(atime._nsec/1e9), /* seconds */
tv_nsec = c.long(atime._nsec%1e9), /* nanoseconds */
},
{
tv_sec = posix.time_t(mtime._nsec/1e9), /* seconds */
tv_nsec = c.long(mtime._nsec%1e9), /* nanoseconds */
},
}
if posix.futimens(__fd(f), &times) != .OK {
return _get_platform_error()
}
return nil
}
_exists :: proc(path: string) -> bool {
TEMP_ALLOCATOR_GUARD()
cpath := temp_cstring(path)
return posix.access(cpath) == .OK
}
_file_stream_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
f := (^File_Impl)(stream_data)
fd := f.fd
switch mode {
case .Read:
if len(p) <= 0 {
return
}
to_read := uint(min(len(p), MAX_RW))
n = i64(posix.read(fd, raw_data(p), to_read))
switch {
case n == 0:
err = .EOF
case n < 0:
err = .Unknown
}
return
case .Read_At:
if len(p) <= 0 {
return
}
if offset < 0 {
err = .Invalid_Offset
return
}
to_read := uint(min(len(p), MAX_RW))
n = i64(posix.pread(fd, raw_data(p), to_read, posix.off_t(offset)))
switch {
case n == 0:
err = .EOF
case n < 0:
err = .Unknown
}
return
case .Write:
p := p
for len(p) > 0 {
to_write := uint(min(len(p), MAX_RW))
if _n := i64(posix.write(fd, raw_data(p), to_write)); _n <= 0 {
err = .Unknown
return
} else {
p = p[_n:]
n += _n
}
}
return
case .Write_At:
p := p
offset := offset
if offset < 0 {
err = .Invalid_Offset
return
}
for len(p) > 0 {
to_write := uint(min(len(p), MAX_RW))
if _n := i64(posix.pwrite(fd, raw_data(p), to_write, posix.off_t(offset))); _n <= 0 {
err = .Unknown
return
} else {
p = p[_n:]
n += _n
offset += _n
}
}
return
case .Seek:
#assert(int(posix.Whence.SET) == int(io.Seek_From.Start))
#assert(int(posix.Whence.CUR) == int(io.Seek_From.Current))
#assert(int(posix.Whence.END) == int(io.Seek_From.End))
n = i64(posix.lseek(fd, posix.off_t(offset), posix.Whence(whence)))
if n < 0 {
err = .Unknown
}
return
case .Size:
stat: posix.stat_t
if posix.fstat(fd, &stat) != .OK {
err = .Unknown
return
}
n = i64(stat.st_size)
return
case .Flush:
ferr := _sync(&f.file)
err = error_to_io_error(ferr)
return
case .Close, .Destroy:
ferr := _close(f)
err = error_to_io_error(ferr)
return
case .Query:
return io.query_utility({.Read, .Read_At, .Write, .Write_At, .Seek, .Size, .Flush, .Close, .Query})
case:
return 0, .Empty
}
}

View File

@@ -0,0 +1,18 @@
//+private
package os2
import "base:runtime"
import "core:sys/posix"
_posix_absolute_path :: proc(fd: posix.FD, name: string, allocator: runtime.Allocator) -> (path: cstring, err: Error) {
F_GETPATH :: 50
buf: [posix.PATH_MAX]byte
if posix.fcntl(fd, posix.FCNTL_Cmd(F_GETPATH), &buf) != 0 {
err = _get_platform_error()
return
}
return clone_to_cstring(string(cstring(&buf[0])), allocator)
}

View File

@@ -0,0 +1,47 @@
//+private
package os2
import "base:runtime"
import "core:c"
import "core:sys/posix"
_posix_absolute_path :: proc(fd: posix.FD, name: string, allocator: runtime.Allocator) -> (path: cstring, err: Error) {
// NOTE(Feoramund): The situation isn't ideal, but this was the best way I
// could find to implement this. There are a couple outstanding bug reports
// regarding the desire to retrieve an absolute path from a handle, but to
// my knowledge, there hasn't been any work done on it.
//
// https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=198570
//
// This may be unreliable, according to a comment from 2023.
KInfo_File :: struct {
structsize: c.int,
type: c.int,
fd: c.int,
ref_count: c.int,
flags: c.int,
pad0: c.int,
offset: i64,
// NOTE(Feoramund): This field represents a complicated union that I am
// avoiding implementing for now. I only need the path data below.
_union: [336]byte,
path: [posix.PATH_MAX]c.char,
}
F_KINFO :: 22
kinfo: KInfo_File
kinfo.structsize = size_of(KInfo_File)
res := posix.fcntl(fd, posix.FCNTL_Cmd(F_KINFO), &kinfo)
if res == -1 {
err = _get_platform_error()
return
}
return clone_to_cstring(string(cstring(&kinfo.path[0])), allocator)
}

View File

@@ -0,0 +1,18 @@
//+private
package os2
import "base:runtime"
import "core:sys/posix"
_posix_absolute_path :: proc(fd: posix.FD, name: string, allocator: runtime.Allocator) -> (path: cstring, err: Error) {
F_GETPATH :: 15
buf: [posix.PATH_MAX]byte
if posix.fcntl(fd, posix.FCNTL_Cmd(F_GETPATH), &buf) != 0 {
err = _get_platform_error()
return
}
return clone_to_cstring(string(cstring(&buf[0])), allocator)
}

View File

@@ -0,0 +1,21 @@
//+private
//+build openbsd
package os2
import "base:runtime"
import "core:sys/posix"
_posix_absolute_path :: proc(fd: posix.FD, name: string, allocator: runtime.Allocator) -> (path: cstring, err: Error) {
TEMP_ALLOCATOR_GUARD()
cname := temp_cstring(name)
buf: [posix.PATH_MAX]byte
path = posix.realpath(cname, raw_data(buf[:]))
if path == nil {
err = _get_platform_error()
return
}
return clone_to_cstring(string(path), allocator)
}

View File

@@ -119,23 +119,20 @@ read_entire_file_from_path :: proc(name: string, allocator: runtime.Allocator) -
@(require_results)
read_entire_file_from_file :: proc(f: ^File, allocator: runtime.Allocator) -> (data: []byte, err: Error) {
size: int
has_size := true
has_size := false
if size64, serr := file_size(f); serr == nil {
if i64(int(size64)) != size64 {
if i64(int(size64)) == size64 {
has_size = true
size = int(size64)
}
} else if serr == .No_Size {
has_size = false
} else {
} else if serr != .No_Size {
return
}
size += 1 // for EOF
// TODO(bill): Is this correct logic?
if has_size {
total: int
data = make([]byte, size, allocator) or_return
for {
for total < len(data) {
n: int
n, err = read(f, data[total:])
total += n
@@ -144,15 +141,16 @@ read_entire_file_from_file :: proc(f: ^File, allocator: runtime.Allocator) -> (d
err = nil
}
data = data[:total]
return
break
}
}
return
} else {
buffer: [1024]u8
out_buffer := make([dynamic]u8, 0, 0, allocator)
total := 0
for {
n: int = ---
n: int
n, err = read(f, buffer[:])
total += n
append_elems(&out_buffer, ..buffer[:total])

View File

@@ -0,0 +1,7 @@
//+private
//+build darwin, netbsd, freebsd, openbsd
package os2
import "base:runtime"
_heap_allocator_proc :: runtime.heap_allocator_proc

View File

@@ -43,7 +43,7 @@ clone_to_cstring :: proc(s: string, allocator: runtime.Allocator) -> (res: cstri
}
@(require_results)
temp_cstring :: proc(s: string) -> (cstring, runtime.Allocator_Error) {
temp_cstring :: proc(s: string) -> (cstring, runtime.Allocator_Error) #optional_allocator_error {
return clone_to_cstring(s, temp_allocator())
}
@@ -76,7 +76,7 @@ concatenate :: proc(strings: []string, allocator: runtime.Allocator) -> (res: st
for s in strings {
n += len(s)
}
buf := make([]byte, n) or_return
buf := make([]byte, n, allocator) or_return
n = 0
for s in strings {
n += copy(buf[n:], s)

View File

@@ -13,13 +13,13 @@ is_path_separator :: proc(c: byte) -> bool {
mkdir :: make_directory
make_directory :: proc(name: string, perm: int) -> Error {
make_directory :: proc(name: string, perm: int = 0o755) -> Error {
return _mkdir(name, perm)
}
mkdir_all :: make_directory_all
make_directory_all :: proc(path: string, perm: int) -> Error {
make_directory_all :: proc(path: string, perm: int = 0o755) -> Error {
return _mkdir_all(path, perm)
}

126
core/os/os2/path_posix.odin Normal file
View File

@@ -0,0 +1,126 @@
//+private
//+build darwin, netbsd, freebsd, openbsd
package os2
import "base:runtime"
import "core:path/filepath"
import "core:sys/posix"
_Path_Separator :: '/'
_Path_Separator_String :: "/"
_Path_List_Separator :: ':'
_is_path_separator :: proc(c: byte) -> bool {
return c == _Path_Separator
}
_mkdir :: proc(name: string, perm: int) -> Error {
TEMP_ALLOCATOR_GUARD()
cname := temp_cstring(name)
if posix.mkdir(cname, transmute(posix.mode_t)posix._mode_t(perm)) != .OK {
return _get_platform_error()
}
return nil
}
_mkdir_all :: proc(path: string, perm: int) -> Error {
if path == "" {
return .Invalid_Path
}
TEMP_ALLOCATOR_GUARD()
if exists(path) {
return .Exist
}
clean_path := filepath.clean(path, temp_allocator())
return internal_mkdir_all(clean_path, perm)
internal_mkdir_all :: proc(path: string, perm: int) -> Error {
a, _ := filepath.split(path)
if a != path {
if len(a) > 1 && a[len(a)-1] == '/' {
a = a[:len(a)-1]
}
internal_mkdir_all(a, perm) or_return
}
err := _mkdir(path, perm)
if err == .Exist { err = nil }
return err
}
}
_remove_all :: proc(path: string) -> Error {
TEMP_ALLOCATOR_GUARD()
cpath := temp_cstring(path)
dir := posix.opendir(cpath)
if dir == nil {
return _get_platform_error()
}
defer posix.closedir(dir)
for {
posix.set_errno(.NONE)
entry := posix.readdir(dir)
if entry == nil {
if errno := posix.errno(); errno != .NONE {
return _get_platform_error()
} else {
break
}
}
cname := cstring(raw_data(entry.d_name[:]))
if cname == "." || cname == ".." {
continue
}
fullpath, _ := concatenate({path, "/", string(cname), "\x00"}, temp_allocator())
if entry.d_type == .DIR {
_remove_all(fullpath[:len(fullpath)-1])
} else {
if posix.unlink(cstring(raw_data(fullpath))) != .OK {
return _get_platform_error()
}
}
}
if posix.rmdir(cpath) != .OK {
return _get_platform_error()
}
return nil
}
_get_working_directory :: proc(allocator: runtime.Allocator) -> (dir: string, err: Error) {
TEMP_ALLOCATOR_GUARD()
buf: [dynamic]byte
buf.allocator = temp_allocator()
size := uint(posix.PATH_MAX)
cwd: cstring
for ; cwd == nil; size *= 2 {
resize(&buf, size)
cwd = posix.getcwd(raw_data(buf), len(buf))
if cwd == nil && posix.errno() != .ERANGE {
err = _get_platform_error()
return
}
}
return clone_string(string(cwd), allocator)
}
_set_working_directory :: proc(dir: string) -> (err: Error) {
TEMP_ALLOCATOR_GUARD()
cdir := temp_cstring(dir)
if posix.chdir(cdir) != .OK {
err = _get_platform_error()
}
return
}

View File

@@ -0,0 +1,37 @@
//+private
//+build darwin, netbsd, freebsd, openbsd
package os2
import "core:sys/posix"
import "core:strings"
_pipe :: proc() -> (r, w: ^File, err: Error) {
fds: [2]posix.FD
if posix.pipe(&fds) != .OK {
err = _get_platform_error()
return
}
r = __new_file(fds[0])
ri := (^File_Impl)(r.impl)
rname := strings.builder_make(file_allocator())
// TODO(laytan): is this on all the posix targets?
strings.write_string(&rname, "/dev/fd/")
strings.write_int(&rname, int(fds[0]))
ri.name = strings.to_string(rname)
ri.cname = strings.to_cstring(&rname)
w = __new_file(fds[1])
wi := (^File_Impl)(w.impl)
wname := strings.builder_make(file_allocator())
// TODO(laytan): is this on all the posix targets?
strings.write_string(&wname, "/dev/fd/")
strings.write_int(&wname, int(fds[1]))
wi.name = strings.to_string(wname)
wi.cname = strings.to_cstring(&wname)
return
}

View File

@@ -14,7 +14,7 @@ TIMEOUT_INFINITE :: time.MIN_DURATION // Note(flysand): Any negative duration wi
*/
args := get_args()
@(private="file", require_results)
@(private="file")
get_args :: proc() -> []string {
result := make([]string, len(runtime.args__), heap_allocator())
for rt_arg, i in runtime.args__ {
@@ -131,6 +131,8 @@ Process_Info_Field :: enum {
Working_Dir,
}
ALL_INFO :: Process_Info_Fields{.Executable_Path, .PPid, .Priority, .Command_Line, .Command_Args, .Environment, .Username, .Working_Dir}
/*
Contains information about the process as obtained by the `process_info()`
procedure.
@@ -166,8 +168,8 @@ Process_Info :: struct {
a process given by `pid`.
Use `free_process_info` to free the memory allocated by this procedure. In
case the function returns an error all temporary allocations would be freed
and as such, calling `free_process_info()` is not needed.
case the function returns an error it may only have been an error for one part
of the information and you would still need to call it to free the other parts.
**Note**: The resulting information may or may contain the fields specified
by the `selection` parameter. Always check whether the returned
@@ -187,8 +189,8 @@ process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator:
the `process` parameter.
Use `free_process_info` to free the memory allocated by this procedure. In
case the function returns an error, all temporary allocations would be freed
and as such, calling `free_process_info` is not needed.
case the function returns an error it may only have been an error for one part
of the information and you would still need to call it to free the other parts.
**Note**: The resulting information may or may contain the fields specified
by the `selection` parameter. Always check whether the returned
@@ -206,9 +208,9 @@ process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields,
This procedure obtains the information, specified by `selection` parameter
about the currently running process.
Use `free_process_info` to free the memory allocated by this function. In
case this function returns an error, all temporary allocations would be
freed and as such calling `free_process_info()` is not needed.
Use `free_process_info` to free the memory allocated by this procedure. In
case the function returns an error it may only have been an error for one part
of the information and you would still need to call it to free the other parts.
**Note**: The resulting information may or may contain the fields specified
by the `selection` parameter. Always check whether the returned
@@ -239,12 +241,16 @@ process_info :: proc {
free_process_info :: proc(pi: Process_Info, allocator: runtime.Allocator) {
delete(pi.executable_path, allocator)
delete(pi.command_line, allocator)
for a in pi.command_args {
delete(a, allocator)
}
delete(pi.command_args, allocator)
for s in pi.environment {
delete(s, allocator)
}
delete(pi.environment, allocator)
delete(pi.working_dir, allocator)
delete(pi.username, allocator)
}
/*

View File

@@ -0,0 +1,69 @@
//+private
//+build darwin, netbsd, freebsd, openbsd
package os2
import "base:runtime"
import "core:time"
import "core:sys/posix"
_exit :: proc "contextless" (code: int) -> ! {
posix.exit(i32(code))
}
_get_uid :: proc() -> int {
return int(posix.getuid())
}
_get_euid :: proc() -> int {
return int(posix.geteuid())
}
_get_gid :: proc() -> int {
return int(posix.getgid())
}
_get_egid :: proc() -> int {
return int(posix.getegid())
}
_get_pid :: proc() -> int {
return int(posix.getpid())
}
_get_ppid :: proc() -> int {
return int(posix.getppid())
}
_process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
return _process_info_by_pid(process.pid, selection, allocator)
}
_current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
return _process_info_by_pid(_get_pid(), selection, allocator)
}
_process_open :: proc(pid: int, flags: Process_Open_Flags) -> (process: Process, err: Error) {
err = .Unsupported
return
}
_Sys_Process_Attributes :: struct {}
_process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
err = .Unsupported
return
}
_process_wait :: proc(process: Process, timeout: time.Duration) -> (process_state: Process_State, err: Error) {
err = .Unsupported
return
}
_process_close :: proc(process: Process) -> Error {
return .Unsupported
}
_process_kill :: proc(process: Process) -> Error {
return .Unsupported
}

View File

@@ -0,0 +1,255 @@
//+private
package os2
import "base:runtime"
import "base:intrinsics"
import "core:bytes"
import "core:sys/darwin"
import "core:sys/posix"
import "core:sys/unix"
foreign import lib "system:System.framework"
foreign lib {
sysctl :: proc(
name: [^]i32, namelen: u32,
oldp: rawptr, oldlenp: ^uint,
newp: rawptr, newlen: uint,
) -> posix.result ---
}
_process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
info.pid = pid
get_pidinfo :: proc(pid: int, selection: Process_Info_Fields) -> (ppid: u32, prio: Maybe(i32), uid: posix.uid_t, ok: bool) {
// Short info is enough and requires less permissions if the priority isn't requested.
if .Priority in selection {
info: darwin.proc_taskallinfo
ret := darwin.proc_pidinfo(posix.pid_t(pid), .TASKALLINFO, 0, &info, size_of(info))
if ret > 0 {
assert(ret == size_of(info))
ppid = info.pbsd.pbi_ppid
prio = info.ptinfo.pti_priority
uid = info.pbsd.pbi_uid
ok = true
return
}
}
// Try short info, requires less permissions, but doesn't give a `nice`.
psinfo: darwin.proc_bsdshortinfo
ret := darwin.proc_pidinfo(posix.pid_t(pid), .SHORTBSDINFO, 0, &psinfo, size_of(psinfo))
if ret > 0 {
assert(ret == size_of(psinfo))
ppid = psinfo.pbsi_ppid
uid = psinfo.pbsi_uid
ok = true
}
return
}
// Thought on errors is: allocation failures return immediately (also why the non-allocation stuff is done first),
// other errors usually mean other parts of the info could be retrieved though, so in those cases we keep trying to get the other information.
pidinfo: {
if selection & {.PPid, .Priority, .Username } != {} {
ppid, mprio, uid, ok := get_pidinfo(pid, selection)
if !ok {
if err == nil {
err = _get_platform_error()
}
break pidinfo
}
if .PPid in selection {
info.ppid = int(ppid)
info.fields += {.PPid}
}
if prio, has_prio := mprio.?; has_prio && .Priority in selection {
info.priority = int(prio)
info.fields += {.Priority}
}
if .Username in selection {
pw := posix.getpwuid(uid)
if pw == nil {
if err == nil {
err = _get_platform_error()
}
break pidinfo
}
info.username = clone_string(string(pw.pw_name), allocator) or_return
info.fields += {.Username}
}
}
}
if .Working_Dir in selection {
pinfo: darwin.proc_vnodepathinfo
ret := darwin.proc_pidinfo(posix.pid_t(pid), .VNODEPATHINFO, 0, &pinfo, size_of(pinfo))
if ret > 0 {
assert(ret == size_of(pinfo))
info.working_dir = clone_string(string(cstring(raw_data(pinfo.pvi_cdir.vip_path[:]))), allocator) or_return
info.fields += {.Working_Dir}
} else if err == nil {
err = _get_platform_error()
}
}
if .Executable_Path in selection {
buffer: [darwin.PIDPATHINFO_MAXSIZE]byte = ---
ret := darwin.proc_pidpath(posix.pid_t(pid), raw_data(buffer[:]), len(buffer))
if ret > 0 {
info.executable_path = clone_string(string(buffer[:ret]), allocator) or_return
info.fields += {.Executable_Path}
} else if err == nil {
err = _get_platform_error()
}
}
args: if selection & { .Command_Line, .Command_Args, .Environment } != {} {
mib := []i32{
unix.CTL_KERN,
unix.KERN_PROCARGS2,
i32(pid),
}
length: uint
if sysctl(raw_data(mib), 3, nil, &length, nil, 0) != .OK {
if err == nil {
err = _get_platform_error()
}
break args
}
buf := make([]byte, length, temp_allocator())
if sysctl(raw_data(mib), 3, raw_data(buf), &length, nil, 0) != .OK {
if err == nil {
err = _get_platform_error()
// Looks like EINVAL is returned here if you don't have permission.
if err == Platform_Error(posix.Errno.EINVAL) {
err = .Permission_Denied
}
}
break args
}
buf = buf[:length]
if len(buf) < 4 {
break args
}
// Layout isn't really documented anywhere, I deduced it to be:
// i32 - argc
// cstring - command name (skipped)
// [^]byte - couple of 0 bytes (skipped)
// [^]cstring - argv (up to argc entries)
// [^]cstring - key=value env entries until the end (many intermittent 0 bytes and entries without `=` we skip here too)
argc := (^i32)(raw_data(buf))^
buf = buf[size_of(i32):]
{
command_line: [dynamic]byte
command_line.allocator = allocator
argv: [dynamic]string
argv.allocator = allocator
defer if err != nil {
for arg in argv { delete(arg, allocator) }
delete(argv)
delete(command_line)
}
_, _ = bytes.split_iterator(&buf, {0})
buf = bytes.trim_left(buf, {0})
first_arg := true
for arg in bytes.split_iterator(&buf, {0}) {
if .Command_Line in selection {
if !first_arg {
append(&command_line, ' ') or_return
}
append(&command_line, ..arg) or_return
}
if .Command_Args in selection {
sarg := clone_string(string(arg), allocator) or_return
append(&argv, sarg) or_return
}
first_arg = false
argc -= 1
if argc == 0 {
break
}
}
if .Command_Line in selection {
info.command_line = string(command_line[:])
info.fields += {.Command_Line}
}
if .Command_Args in selection {
info.command_args = argv[:]
info.fields += {.Command_Args}
}
}
if .Environment in selection {
environment: [dynamic]string
environment.allocator = allocator
defer if err != nil {
for entry in environment { delete(entry, allocator) }
delete(environment)
}
for entry in bytes.split_iterator(&buf, {0}) {
if bytes.index_byte(entry, '=') > -1 {
sentry := clone_string(string(entry), allocator) or_return
append(&environment, sentry) or_return
}
}
info.environment = environment[:]
info.fields += {.Environment}
}
}
// Fields were requested that we didn't add.
if err == nil && selection - info.fields != {} {
err = .Unsupported
}
return
}
_process_list :: proc(allocator: runtime.Allocator) -> (list: []int, err: Error) {
ret := darwin.proc_listallpids(nil, 0)
if ret < 0 {
err = _get_platform_error()
return
}
TEMP_ALLOCATOR_GUARD()
buffer := make([]i32, ret, temp_allocator())
ret = darwin.proc_listallpids(raw_data(buffer), ret*size_of(i32))
if ret < 0 {
err = _get_platform_error()
return
}
list = make([]int, ret, allocator) or_return
#no_bounds_check for &entry, i in list {
entry = int(buffer[i])
}
return
}

View File

@@ -0,0 +1,15 @@
//+private
//+build netbsd, openbsd, freebsd
package os2
import "base:runtime"
_process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (info: Process_Info, err: Error) {
err = .Unsupported
return
}
_process_list :: proc(allocator: runtime.Allocator) -> (list: []int, err: Error) {
err = .Unsupported
return
}

View File

@@ -30,8 +30,8 @@ file_info_clone :: proc(fi: File_Info, allocator: runtime.Allocator) -> (cloned:
}
file_info_slice_delete :: proc(infos: []File_Info, allocator: runtime.Allocator) {
for i := len(infos)-1; i >= 0; i -= 1 {
file_info_delete(infos[i], allocator)
#reverse for info in infos {
file_info_delete(info, allocator)
}
delete(infos, allocator)
}

137
core/os/os2/stat_posix.odin Normal file
View File

@@ -0,0 +1,137 @@
//+private
//+build darwin, netbsd, freebsd, openbsd
package os2
import "base:runtime"
import "core:path/filepath"
import "core:sys/posix"
import "core:time"
internal_stat :: proc(stat: posix.stat_t, fullpath: string) -> (fi: File_Info) {
fi.fullpath = fullpath
fi.name = filepath.base(fi.fullpath)
fi.inode = u128(stat.st_ino)
fi.size = i64(stat.st_size)
fi.mode = int(transmute(posix._mode_t)(stat.st_mode - posix.S_IFMT))
fi.type = .Undetermined
switch {
case posix.S_ISBLK(stat.st_mode):
fi.type = .Block_Device
case posix.S_ISCHR(stat.st_mode):
fi.type = .Character_Device
case posix.S_ISDIR(stat.st_mode):
fi.type = .Directory
case posix.S_ISFIFO(stat.st_mode):
fi.type = .Named_Pipe
case posix.S_ISLNK(stat.st_mode):
fi.type = .Symlink
case posix.S_ISREG(stat.st_mode):
fi.type = .Regular
case posix.S_ISSOCK(stat.st_mode):
fi.type = .Socket
}
fi.creation_time = timespec_time(stat.st_birthtimespec)
fi.modification_time = timespec_time(stat.st_mtim)
fi.access_time = timespec_time(stat.st_atim)
timespec_time :: proc(t: posix.timespec) -> time.Time {
return time.Time{_nsec = i64(t.tv_sec) * 1e9 + i64(t.tv_nsec)}
}
return
}
_fstat :: proc(f: ^File, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
if f == nil || f.impl == nil {
err = .Invalid_File
return
}
impl := (^File_Impl)(f.impl)
stat: posix.stat_t
if posix.fstat(impl.fd, &stat) != .OK {
err = _get_platform_error()
return
}
fullpath := clone_string(impl.name, allocator) or_return
return internal_stat(stat, fullpath), nil
}
_stat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
if name == "" {
err = .Invalid_Path
return
}
TEMP_ALLOCATOR_GUARD()
cname := temp_cstring(name) or_return
fd := posix.open(cname, {})
if fd == -1 {
err = _get_platform_error()
return
}
defer posix.close(fd)
fullpath := _posix_absolute_path(fd, name, allocator) or_return
stat: posix.stat_t
if posix.stat(fullpath, &stat) != .OK {
err = _get_platform_error()
return
}
return internal_stat(stat, string(fullpath)), nil
}
_lstat :: proc(name: string, allocator: runtime.Allocator) -> (fi: File_Info, err: Error) {
if name == "" {
err = .Invalid_Path
return
}
TEMP_ALLOCATOR_GUARD()
// NOTE: can't use realpath or open (+ fcntl F_GETPATH) here because it tries to resolve symlinks.
// NOTE: This might not be correct when given "/symlink/foo.txt",
// you would want that to resolve "/symlink", but not resolve "foo.txt".
fullpath := filepath.clean(name, temp_allocator())
assert(len(fullpath) > 0)
switch {
case fullpath[0] == '/':
// nothing.
case fullpath == ".":
fullpath = getwd(temp_allocator()) or_return
case len(fullpath) > 1 && fullpath[0] == '.' && fullpath[1] == '/':
fullpath = fullpath[2:]
fallthrough
case:
fullpath = concatenate({
getwd(temp_allocator()) or_return,
"/",
fullpath,
}, temp_allocator()) or_return
}
stat: posix.stat_t
if posix.lstat(temp_cstring(fullpath), &stat) != .OK {
err = _get_platform_error()
return
}
fullpath = clone_string(fullpath, allocator) or_return
return internal_stat(stat, fullpath), nil
}
_same_file :: proc(fi1, fi2: File_Info) -> bool {
return fi1.fullpath == fi2.fullpath
}

View File

@@ -44,6 +44,7 @@ full_path_from_name :: proc(name: string, allocator: runtime.Allocator) -> (path
if name == "" {
name = "."
}
TEMP_ALLOCATOR_GUARD()
p := win32_utf8_to_utf16(name, temp_allocator()) or_return
@@ -127,7 +128,9 @@ _cleanpath_from_handle :: proc(f: ^File, allocator: runtime.Allocator) -> (strin
if n == 0 {
return "", _get_platform_error()
}
TEMP_ALLOCATOR_GUARD()
buf := make([]u16, max(n, 260)+1, temp_allocator())
n = win32.GetFinalPathNameByHandleW(h, raw_data(buf), u32(len(buf)), 0)
return _cleanpath_from_buf(buf[:n], allocator)
@@ -143,7 +146,9 @@ _cleanpath_from_handle_u16 :: proc(f: ^File) -> ([]u16, Error) {
if n == 0 {
return nil, _get_platform_error()
}
TEMP_ALLOCATOR_GUARD()
buf := make([]u16, max(n, 260)+1, temp_allocator())
n = win32.GetFinalPathNameByHandleW(h, raw_data(buf), u32(len(buf)), 0)
return _cleanpath_strip_prefix(buf[:n]), nil

View File

@@ -10,7 +10,7 @@ MAX_ATTEMPTS :: 1<<13 // Should be enough for everyone, right?
// Opens the file for reading and writing, with 0o666 permissions, and returns the new `^File`.
// The filename is generated by taking a pattern, and adding a randomized string to the end.
// If the pattern includes an "*", the random string replaces the last "*".
// If `dir` is an empty tring, `temp_directory()` will be used.
// If `dir` is an empty string, `temp_directory()` will be used.
//
// The caller must `close` the file once finished with.
@(require_results)

View File

@@ -0,0 +1,20 @@
//+private
//+build darwin, netbsd, freebsd, openbsd
package os2
import "base:runtime"
@(require)
import "core:sys/posix"
_temp_dir :: proc(allocator: runtime.Allocator) -> (string, runtime.Allocator_Error) {
if tmp, ok := _lookup_env("TMPDIR", allocator); ok {
return tmp, nil
}
when #defined(posix.P_tmpdir) {
return clone_string(posix.P_tmpdir, allocator)
}
return clone_string("/tmp/", allocator)
}

View File

@@ -42,7 +42,7 @@ user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Erro
case .Darwin:
dir = get_env("HOME", allocator)
if dir != "" {
dir = concatenate({dir, "/Library/Application Support"}, allocator) or_return
dir = concatenate({dir, "/.config"}, allocator) or_return
}
case: // All other UNIX systems
dir = get_env("XDG_CACHE_HOME", allocator)

View File

@@ -584,7 +584,7 @@ F_GETPATH :: 50 // return the full path of the fd
foreign libc {
@(link_name="__error") __error :: proc() -> ^c.int ---
@(link_name="open") _unix_open :: proc(path: cstring, flags: i32, #c_vararg args: ..any) -> Handle ---
@(link_name="open") _unix_open :: proc(path: cstring, flags: i32, mode: u16) -> Handle ---
@(link_name="close") _unix_close :: proc(handle: Handle) -> c.int ---
@(link_name="read") _unix_read :: proc(handle: Handle, buffer: rawptr, count: c.size_t) -> int ---
@(link_name="write") _unix_write :: proc(handle: Handle, buffer: rawptr, count: c.size_t) -> int ---
@@ -628,23 +628,23 @@ foreign libc {
@(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring ---
@(link_name="chdir") _unix_chdir :: proc(buf: cstring) -> c.int ---
@(link_name="mkdir") _unix_mkdir :: proc(buf: cstring, mode: u16) -> c.int ---
@(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
@(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: [^]byte = nil) -> cstring ---
@(link_name="strerror") _darwin_string_error :: proc(num : c.int) -> cstring ---
@(link_name="sysctlbyname") _sysctlbyname :: proc(path: cstring, oldp: rawptr, oldlenp: rawptr, newp: rawptr, newlen: int) -> c.int ---
@(link_name="socket") _unix_socket :: proc(domain: int, type: int, protocol: int) -> int ---
@(link_name="listen") _unix_listen :: proc(socket: int, backlog: int) -> int ---
@(link_name="accept") _unix_accept :: proc(socket: int, addr: rawptr, addr_len: rawptr) -> int ---
@(link_name="connect") _unix_connect :: proc(socket: int, addr: rawptr, addr_len: socklen_t) -> int ---
@(link_name="bind") _unix_bind :: proc(socket: int, addr: rawptr, addr_len: socklen_t) -> int ---
@(link_name="setsockopt") _unix_setsockopt :: proc(socket: int, level: int, opt_name: int, opt_val: rawptr, opt_len: socklen_t) -> int ---
@(link_name="getsockopt") _unix_getsockopt :: proc(socket: int, level: int, opt_name: int, opt_val: rawptr, opt_len: socklen_t) -> int ---
@(link_name="recvfrom") _unix_recvfrom :: proc(socket: int, buffer: rawptr, buffer_len: c.size_t, flags: int, addr: rawptr, addr_len: ^socklen_t) -> c.ssize_t ---
@(link_name="recv") _unix_recv :: proc(socket: int, buffer: rawptr, buffer_len: c.size_t, flags: int) -> c.ssize_t ---
@(link_name="sendto") _unix_sendto :: proc(socket: int, buffer: rawptr, buffer_len: c.size_t, flags: int, addr: rawptr, addr_len: socklen_t) -> c.ssize_t ---
@(link_name="send") _unix_send :: proc(socket: int, buffer: rawptr, buffer_len: c.size_t, flags: int) -> c.ssize_t ---
@(link_name="shutdown") _unix_shutdown :: proc(socket: int, how: int) -> int ---
@(link_name="socket") _unix_socket :: proc(domain: c.int, type: c.int, protocol: c.int) -> c.int ---
@(link_name="listen") _unix_listen :: proc(socket: c.int, backlog: c.int) -> c.int ---
@(link_name="accept") _unix_accept :: proc(socket: c.int, addr: rawptr, addr_len: rawptr) -> c.int ---
@(link_name="connect") _unix_connect :: proc(socket: c.int, addr: rawptr, addr_len: socklen_t) -> c.int ---
@(link_name="bind") _unix_bind :: proc(socket: c.int, addr: rawptr, addr_len: socklen_t) -> c.int ---
@(link_name="setsockopt") _unix_setsockopt :: proc(socket: c.int, level: c.int, opt_name: c.int, opt_val: rawptr, opt_len: socklen_t) -> c.int ---
@(link_name="getsockopt") _unix_getsockopt :: proc(socket: c.int, level: c.int, opt_name: c.int, opt_val: rawptr, opt_len: ^socklen_t) -> c.int ---
@(link_name="recvfrom") _unix_recvfrom :: proc(socket: c.int, buffer: rawptr, buffer_len: c.size_t, flags: c.int, addr: rawptr, addr_len: ^socklen_t) -> c.ssize_t ---
@(link_name="recv") _unix_recv :: proc(socket: c.int, buffer: rawptr, buffer_len: c.size_t, flags: c.int) -> c.ssize_t ---
@(link_name="sendto") _unix_sendto :: proc(socket: c.int, buffer: rawptr, buffer_len: c.size_t, flags: c.int, addr: rawptr, addr_len: socklen_t) -> c.ssize_t ---
@(link_name="send") _unix_send :: proc(socket: c.int, buffer: rawptr, buffer_len: c.size_t, flags: c.int) -> c.ssize_t ---
@(link_name="shutdown") _unix_shutdown :: proc(socket: c.int, how: c.int) -> c.int ---
@(link_name="getifaddrs") _getifaddrs :: proc(ifap: ^^ifaddrs) -> (c.int) ---
@(link_name="freeifaddrs") _freeifaddrs :: proc(ifa: ^ifaddrs) ---
@@ -661,9 +661,9 @@ when ODIN_ARCH != .arm64 {
}
foreign dl {
@(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: int) -> rawptr ---
@(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) -> int ---
@(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> c.int ---
@(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---
}
@@ -1040,10 +1040,9 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
if path_ptr == nil {
return "", get_last_error()
}
defer _unix_free(path_ptr)
defer _unix_free(rawptr(path_ptr))
path_cstr := cast(cstring)path_ptr
path = strings.clone(string(path_cstr))
path = strings.clone(string(path_ptr))
return path, nil
}
@@ -1154,7 +1153,7 @@ current_thread_id :: proc "contextless" () -> int {
dlopen :: proc(filename: string, flags: int) -> rawptr {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(filename, context.temp_allocator)
handle := _unix_dlopen(cstr, flags)
handle := _unix_dlopen(cstr, c.int(flags))
return handle
}
@(require_results)
@@ -1208,26 +1207,24 @@ _alloc_command_line_arguments :: proc() -> []string {
return res
}
@(require_results)
socket :: proc(domain: int, type: int, protocol: int) -> (Socket, Error) {
result := _unix_socket(domain, type, protocol)
result := _unix_socket(c.int(domain), c.int(type), c.int(protocol))
if result < 0 {
return 0, get_last_error()
}
return Socket(result), nil
}
@(require_results)
connect :: proc(sd: Socket, addr: ^SOCKADDR, len: socklen_t) -> Error {
result := _unix_connect(int(sd), addr, len)
result := _unix_connect(c.int(sd), addr, len)
if result < 0 {
return get_last_error()
}
return nil
}
bind :: proc(sd: Socket, addr: ^SOCKADDR, len: socklen_t) -> Error {
result := _unix_bind(int(sd), addr, len)
bind :: proc(sd: Socket, addr: ^SOCKADDR, len: socklen_t) -> (Error) {
result := _unix_bind(c.int(sd), addr, len)
if result < 0 {
return get_last_error()
}
@@ -1235,15 +1232,15 @@ bind :: proc(sd: Socket, addr: ^SOCKADDR, len: socklen_t) -> Error {
}
accept :: proc(sd: Socket, addr: ^SOCKADDR, len: rawptr) -> (Socket, Error) {
result := _unix_accept(int(sd), rawptr(addr), len)
result := _unix_accept(c.int(sd), rawptr(addr), len)
if result < 0 {
return 0, get_last_error()
}
return Socket(result), nil
}
listen :: proc(sd: Socket, backlog: int) -> Error {
result := _unix_listen(int(sd), backlog)
listen :: proc(sd: Socket, backlog: int) -> (Error) {
result := _unix_listen(c.int(sd), c.int(backlog))
if result < 0 {
return get_last_error()
}
@@ -1251,7 +1248,7 @@ listen :: proc(sd: Socket, backlog: int) -> Error {
}
setsockopt :: proc(sd: Socket, level: int, optname: int, optval: rawptr, optlen: socklen_t) -> Error {
result := _unix_setsockopt(int(sd), level, optname, optval, optlen)
result := _unix_setsockopt(c.int(sd), c.int(level), c.int(optname), optval, optlen)
if result < 0 {
return get_last_error()
}
@@ -1259,7 +1256,8 @@ setsockopt :: proc(sd: Socket, level: int, optname: int, optval: rawptr, optlen:
}
getsockopt :: proc(sd: Socket, level: int, optname: int, optval: rawptr, optlen: socklen_t) -> Error {
result := _unix_getsockopt(int(sd), level, optname, optval, optlen)
optlen := optlen
result := _unix_getsockopt(c.int(sd), c.int(level), c.int(optname), optval, &optlen)
if result < 0 {
return get_last_error()
}
@@ -1267,7 +1265,7 @@ getsockopt :: proc(sd: Socket, level: int, optname: int, optval: rawptr, optlen:
}
recvfrom :: proc(sd: Socket, data: []byte, flags: int, addr: ^SOCKADDR, addr_size: ^socklen_t) -> (u32, Error) {
result := _unix_recvfrom(int(sd), raw_data(data), len(data), flags, addr, addr_size)
result := _unix_recvfrom(c.int(sd), raw_data(data), len(data), c.int(flags), addr, addr_size)
if result < 0 {
return 0, get_last_error()
}
@@ -1275,7 +1273,7 @@ recvfrom :: proc(sd: Socket, data: []byte, flags: int, addr: ^SOCKADDR, addr_siz
}
recv :: proc(sd: Socket, data: []byte, flags: int) -> (u32, Error) {
result := _unix_recv(int(sd), raw_data(data), len(data), flags)
result := _unix_recv(c.int(sd), raw_data(data), len(data), c.int(flags))
if result < 0 {
return 0, get_last_error()
}
@@ -1283,7 +1281,7 @@ recv :: proc(sd: Socket, data: []byte, flags: int) -> (u32, Error) {
}
sendto :: proc(sd: Socket, data: []u8, flags: int, addr: ^SOCKADDR, addrlen: socklen_t) -> (u32, Error) {
result := _unix_sendto(int(sd), raw_data(data), len(data), flags, addr, addrlen)
result := _unix_sendto(c.int(sd), raw_data(data), len(data), c.int(flags), addr, addrlen)
if result < 0 {
return 0, get_last_error()
}
@@ -1291,15 +1289,15 @@ sendto :: proc(sd: Socket, data: []u8, flags: int, addr: ^SOCKADDR, addrlen: soc
}
send :: proc(sd: Socket, data: []byte, flags: int) -> (u32, Error) {
result := _unix_send(int(sd), raw_data(data), len(data), 0)
result := _unix_send(c.int(sd), raw_data(data), len(data), 0)
if result < 0 {
return 0, get_last_error()
}
return u32(result), nil
}
shutdown :: proc(sd: Socket, how: int) -> Error {
result := _unix_shutdown(int(sd), how)
shutdown :: proc(sd: Socket, how: int) -> (Error) {
result := _unix_shutdown(c.int(sd), c.int(how))
if result < 0 {
return get_last_error()
}

View File

@@ -371,7 +371,7 @@ F_KINFO :: 22
foreign libc {
@(link_name="__error") __Error_location :: proc() -> ^c.int ---
@(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle ---
@(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.uint16_t) -> 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 ---
@@ -388,7 +388,7 @@ foreign libc {
@(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="fcntl") _unix_fcntl :: proc(fd: Handle, cmd: c.int, arg: uintptr) -> c.int ---
@(link_name="fcntl") _unix_fcntl :: proc(fd: Handle, cmd: c.int, #c_vararg args: ..any) -> c.int ---
@(link_name="dup") _unix_dup :: proc(fd: Handle) -> Handle ---
@(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir ---
@@ -402,7 +402,7 @@ foreign libc {
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---
@(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
@(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: [^]byte = nil) -> cstring ---
@(link_name="sysctlbyname") _sysctlbyname :: proc(path: cstring, oldp: rawptr, oldlenp: rawptr, newp: rawptr, newlen: int) -> c.int ---
@(link_name="exit") _unix_exit :: proc(status: c.int) -> ! ---
@@ -430,7 +430,7 @@ get_last_error :: proc "contextless" () -> Error {
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Error) {
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
cstr := strings.clone_to_cstring(path, context.temp_allocator)
handle := _unix_open(cstr, c.int(flags), c.int(mode))
handle := _unix_open(cstr, c.int(flags), u16(mode))
if handle == -1 {
return INVALID_HANDLE, get_last_error()
}
@@ -786,10 +786,10 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
if path_ptr == nil {
return "", get_last_error()
}
defer _unix_free(path_ptr)
defer _unix_free(rawptr(path_ptr))
path = strings.clone(string(cstring(path_ptr)))
path = strings.clone(string(path_ptr))
return path, nil
}

View File

@@ -491,7 +491,7 @@ foreign libc {
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---
@(link_name="putenv") _unix_putenv :: proc(cstring) -> c.int ---
@(link_name="setenv") _unix_setenv :: proc(key: cstring, value: cstring, overwrite: c.int) -> c.int ---
@(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
@(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: [^]byte = nil) -> cstring ---
@(link_name="exit") _unix_exit :: proc(status: c.int) -> ! ---
}
@@ -917,9 +917,9 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
if path_ptr == nil {
return "", get_last_error()
}
defer _unix_free(path_ptr)
defer _unix_free(rawptr(path_ptr))
path = strings.clone(string(cstring(path_ptr)))
path = strings.clone(string(path_ptr))
return path, nil
}

View File

@@ -330,7 +330,7 @@ dev_t :: u64
ino_t :: u64
nlink_t :: u32
off_t :: i64
mode_t :: u16
mode_t :: u32
pid_t :: u32
uid_t :: u32
gid_t :: u32
@@ -440,7 +440,7 @@ foreign libc {
@(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="fcntl") _unix_fcntl :: proc(fd: Handle, cmd: c.int, arg: uintptr) -> c.int ---
@(link_name="fcntl") _unix_fcntl :: proc(fd: Handle, cmd: c.int, #c_vararg args: ..any) -> c.int ---
@(link_name="dup") _unix_dup :: proc(fd: Handle) -> Handle ---
@(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir ---
@@ -454,7 +454,7 @@ foreign libc {
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---
@(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
@(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: [^]byte = nil) -> cstring ---
@(link_name="sysctlbyname") _sysctlbyname :: proc(path: cstring, oldp: rawptr, oldlenp: rawptr, newp: rawptr, newlen: int) -> c.int ---
@(link_name="exit") _unix_exit :: proc(status: c.int) -> ! ---
@@ -832,9 +832,9 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
if path_ptr == nil {
return "", get_last_error()
}
defer _unix_free(path_ptr)
defer _unix_free(rawptr(path_ptr))
path = strings.clone(string(cstring(path_ptr)))
path = strings.clone(string(path_ptr))
return path, nil
}

View File

@@ -379,7 +379,7 @@ foreign libc {
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---
@(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
@(link_name="realpath") _unix_realpath :: proc(path: cstring, resolved_path: [^]byte = nil) -> cstring ---
@(link_name="exit") _unix_exit :: proc(status: c.int) -> ! ---
@@ -746,9 +746,9 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
if path_ptr == nil {
return "", get_last_error()
}
defer _unix_free(path_ptr)
defer _unix_free(rawptr(path_ptr))
path = strings.clone(string(cstring(path_ptr)))
path = strings.clone(string(path_ptr))
return path, nil
}

View File

@@ -32,10 +32,9 @@ abs :: proc(path: string, allocator := context.allocator) -> (string, bool) {
if path_ptr == nil {
return "", __error()^ == 0
}
defer _unix_free(path_ptr)
defer _unix_free(rawptr(path_ptr))
path_cstr := cstring(path_ptr)
path_str := strings.clone(string(path_cstr), allocator)
path_str := strings.clone(string(path_ptr), allocator)
return path_str, true
}
@@ -52,7 +51,7 @@ join :: proc(elems: []string, allocator := context.allocator) -> string {
@(private)
foreign libc {
realpath :: proc(path: cstring, resolved_path: rawptr) -> rawptr ---
realpath :: proc(path: cstring, resolved_path: [^]byte = nil) -> cstring ---
@(link_name="free") _unix_free :: proc(ptr: rawptr) ---
}

View File

@@ -5,22 +5,7 @@ package spall
// Only for types.
import "core:os"
when ODIN_OS == .Darwin {
foreign import libc "system:System.framework"
} else {
foreign import libc "system:c"
}
timespec :: struct {
tv_sec: i64, // seconds
tv_nsec: i64, // nanoseconds
}
foreign libc {
__error :: proc() -> ^i32 ---
@(link_name="write") _unix_write :: proc(handle: os.Handle, buffer: rawptr, count: uint) -> int ---
@(link_name="clock_gettime") _unix_clock_gettime :: proc(clock_id: u64, timespec: ^timespec) -> i32 ---
}
import "core:sys/posix"
MAX_RW :: 0x7fffffff
@@ -32,7 +17,7 @@ _write :: proc "contextless" (fd: os.Handle, data: []byte) -> (n: int, err: os.E
for n < len(data) {
chunk := data[:min(len(data), MAX_RW)]
written := _unix_write(fd, raw_data(chunk), len(chunk))
written := posix.write(posix.FD(fd), raw_data(chunk), len(chunk))
if written < 0 {
return n, os.get_last_error()
}
@@ -42,11 +27,17 @@ _write :: proc "contextless" (fd: os.Handle, data: []byte) -> (n: int, err: os.E
return n, nil
}
CLOCK_MONOTONIC_RAW :: 4 // NOTE(tetra): "RAW" means: Not adjusted by NTP.
// NOTE(tetra): "RAW" means: Not adjusted by NTP.
when ODIN_OS == .Darwin {
CLOCK :: posix.Clock(4) // CLOCK_MONOTONIC_RAW
} else {
// It looks like the BSDs don't have a CLOCK_MONOTONIC_RAW equivalent.
CLOCK :: posix.Clock.MONOTONIC
}
@(no_instrumentation)
_tick_now :: proc "contextless" () -> (ns: i64) {
t: timespec
_unix_clock_gettime(CLOCK_MONOTONIC_RAW, &t)
return t.tv_sec*1e9 + t.tv_nsec
t: posix.timespec
posix.clock_gettime(CLOCK, &t)
return i64(t.tv_sec)*1e9 + i64(t.tv_nsec)
}

168
core/sys/darwin/proc.odin Normal file
View File

@@ -0,0 +1,168 @@
package darwin
import "base:intrinsics"
import "core:sys/posix"
foreign import lib "system:System.framework"
// Incomplete bindings to the proc API on MacOS, add to when needed.
foreign lib {
proc_pidinfo :: proc(pid: posix.pid_t, flavor: PID_Info_Flavor, arg: i64, buffer: rawptr, buffersize: i32) -> i32 ---
proc_pidpath :: proc(pid: posix.pid_t, buffer: [^]byte, buffersize: u32) -> i32 ---
proc_listallpids :: proc(buffer: [^]i32, buffersize: i32) -> i32 ---
}
MAXCOMLEN :: 16
proc_bsdinfo :: struct {
pbi_flags: PBI_Flags,
pbi_status: u32,
pbi_xstatus: u32,
pbi_pid: u32,
pbi_ppid: u32,
pbi_uid: posix.uid_t,
pbi_gid: posix.gid_t,
pbi_ruid: posix.uid_t,
pbi_rgid: posix.gid_t,
pbi_svuid: posix.uid_t,
pbi_svgid: posix.gid_t,
rfu_1: u32,
pbi_comm: [MAXCOMLEN]byte `fmt:"s,0"`,
pbi_name: [2 * MAXCOMLEN]byte `fmt:"s,0"`,
pbi_nfiles: u32,
pbi_pgid: u32,
pbi_pjobc: u32,
e_tdev: u32,
e_tpgid: u32,
pbi_nice: i32,
pbi_start_tvsec: u64,
pbi_start_tvusec: u64,
}
proc_bsdshortinfo :: struct {
pbsi_pid: u32,
pbsi_ppid: u32,
pbsi_pgid: u32,
pbsi_status: u32,
pbsi_comm: [MAXCOMLEN]byte `fmt:"s,0"`,
pbsi_flags: PBI_Flags,
pbsi_uid: posix.uid_t,
pbsi_gid: posix.gid_t,
pbsi_ruid: posix.uid_t,
pbsi_rgid: posix.gid_t,
pbsi_svuid: posix.uid_t,
pbsi_svgid: posix.gid_t,
pbsi_rfu: u32,
}
proc_vnodepathinfo :: struct {
pvi_cdir: vnode_info_path,
pvi_rdir: vnode_info_path,
}
vnode_info_path :: struct {
vip_vi: vnode_info,
vip_path: [posix.PATH_MAX]byte,
}
vnode_info :: struct {
vi_stat: vinfo_stat,
vi_type: i32,
vi_pad: i32,
vi_fsid: fsid_t,
}
vinfo_stat :: struct {
vst_dev: u32,
vst_mode: u16,
vst_nlink: u16,
vst_ino: u64,
vst_uid: posix.uid_t,
vst_gid: posix.gid_t,
vst_atime: i64,
vst_atimensec: i64,
vst_mtime: i64,
vst_mtimensec: i64,
vst_ctime: i64,
vst_ctimensec: i64,
vst_birthtime: i64,
vst_birthtimensec: i64,
vst_size: posix.off_t,
vst_blocks: i64,
vst_blksize: i32,
vst_flags: u32,
vst_gen: u32,
vst_rdev: u32,
vst_qspare: [2]i64,
}
proc_taskinfo :: struct {
pti_virtual_size: u64 `fmt:"M"`,
pti_resident_size: u64 `fmt:"M"`,
pti_total_user: u64,
pti_total_system: u64,
pti_threads_user: u64,
pti_threads_system: u64,
pti_policy: i32,
pti_faults: i32,
pti_pageins: i32,
pti_cow_faults: i32,
pti_messages_sent: i32,
pti_messages_received: i32,
pti_syscalls_mach: i32,
pti_syscalls_unix: i32,
pti_csw: i32,
pti_threadnum: i32,
pti_numrunning: i32,
pti_priority: i32,
}
proc_taskallinfo :: struct {
pbsd: proc_bsdinfo,
ptinfo: proc_taskinfo,
}
fsid_t :: distinct [2]i32
PBI_Flag_Bits :: enum u32 {
SYSTEM = intrinsics.constant_log2(0x0001),
TRACED = intrinsics.constant_log2(0x0002),
INEXIT = intrinsics.constant_log2(0x0004),
PWAIT = intrinsics.constant_log2(0x0008),
LP64 = intrinsics.constant_log2(0x0010),
SLEADER = intrinsics.constant_log2(0x0020),
CTTY = intrinsics.constant_log2(0x0040),
CONTROLT = intrinsics.constant_log2(0x0080),
THCWD = intrinsics.constant_log2(0x0100),
PC_THROTTLE = intrinsics.constant_log2(0x0200),
PC_SUSP = intrinsics.constant_log2(0x0400),
PC_KILL = intrinsics.constant_log2(0x0600),
PA_THROTTLE = intrinsics.constant_log2(0x0800),
PA_SUSP = intrinsics.constant_log2(0x1000),
PA_PSUGID = intrinsics.constant_log2(0x2000),
EXEC = intrinsics.constant_log2(0x4000),
}
PBI_Flags :: bit_set[PBI_Flag_Bits; u32]
PID_Info_Flavor :: enum i32 {
LISTFDS = 1,
TASKALLINFO,
BSDINFO,
TASKINFO,
THREADINFO,
LISTTHREADS,
REGIONINFO,
REGIONPATHINFO,
VNODEPATHINFO,
THREADPATHINFO,
PATHINFO,
WORKQUEUEINFO,
SHORTBSDINFO,
LISTFILEPORTS,
THREADID64INFO,
RUSAGE,
}
PIDPATHINFO_MAXSIZE :: 4*posix.PATH_MAX

View File

@@ -0,0 +1,58 @@
package posix
import "core:c"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// arpa/inet.h - definitions for internet operations
foreign lib {
// Use Odin's native big endian types `u32be` and `u16be` instead.
// htonl :: proc(c.uint32_t) -> c.uint32_t ---
// htons :: proc(c.uint16_t) -> c.uint16_t ---
// ntohl :: proc(c.uint32_t) -> c.uint32_t ---
// ntohs :: proc(c.uint16_t) -> c.uint16_t ---
// Use of this function is problematic because -1 is a valid address (255.255.255.255).
// Avoid its use in favor of inet_aton(), inet_pton(3), or getaddrinfo(3) which provide a cleaner way to indicate error return.
// inet_addr :: proc(cstring) -> in_addr_t ---
// Convert the Internet host address specified by in to a string in the Internet standard dot notation.
//
// NOTE: returns a static string overwritten by further calls.
//
// [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/inet_ntoa.html ]]
inet_ntoa :: proc(in_addr) -> cstring ---
// Convert a numeric address into a text string suitable for presentation.
//
// Returns `nil` and sets `errno` on failure.
//
// [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/inet_ntop.html ]]
inet_ntop :: proc(
af: AF, // INET or INET6
src: rawptr, // either ^in_addr or ^in_addr6
dst: [^]byte, // use `INET_ADDRSTRLEN` or `INET6_ADDRSTRLEN` for minimum lengths
size: socklen_t,
) -> cstring ---
// Convert an address in its standard text presentation form into its numeric binary form.
//
// [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/inet_ntop.html ]]
inet_pton :: proc(
af: AF, // INET or INET6
src: cstring,
dst: rawptr, // either ^in_addr or ^in_addr6
size: socklen_t, // size_of(dst^)
) -> pton_result ---
}
pton_result :: enum c.int {
AFNOSUPPORT = -1,
INVALID = 0,
SUCCESS = 1,
}

205
core/sys/posix/dirent.odin Normal file
View File

@@ -0,0 +1,205 @@
package posix
import "core:c"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// dirent.h - format of directory entries
foreign lib {
/*
can be used as the comparison function for the scandir() function to sort the directory entries, d1 and d2, into alphabetical order.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/scandir.html ]]
*/
@(link_name=LALPHASORT)
alphasort :: proc([^]^dirent, [^]^dirent) -> c.int ---
/*
Scan the directory dir, calling the function referenced by sel on each directory entry.
Example:
list: [^]^posix.dirent
ret := posix.scandir(#directory, &list, nil, posix.alphasort)
if ret < 0 {
panic(string(posix.strerror(posix.errno())))
}
defer posix.free(list)
entries := list[:ret]
for entry in entries {
log.info(entry)
posix.free(entry)
}
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/scandir.html ]]
*/
@(link_name=LSCANDIR)
scandir :: proc(
dir: cstring,
sel: ^[^]^dirent,
filter: proc "c" (^dirent) -> b32 = nil,
compar: proc "c" ([^]^dirent, [^]^dirent) -> c.int = alphasort,
) -> c.int ---
/*
Close the directory stream referred to by the argument dirp.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/closedir.html ]]
*/
closedir :: proc(dirp: DIR) -> result ---
/*
Return a file descriptor referring to the same directory as the dirp argument.
// TODO: this is a macro on NetBSD?
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/dirfd.html ]]
*/
dirfd :: proc(dirp: DIR) -> FD ---
/*
Equivalent to the opendir() function except that the directory is specified by a file descriptor
rather than by a name.
The file offset associated with the file descriptor at the time of the call determines
which entries are returned.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fdopendir.html ]]
*/
@(link_name="fdopendir" + INODE_SUFFIX)
fdopendir :: proc(dirp: FD) -> DIR ---
/*
Open a directory stream corresponding to the directory named by the dirname argument.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fdopendir.html ]]
*/
@(link_name=LOPENDIR)
opendir :: proc(path: cstring) -> DIR ---
/*
Returns a pointer to a structure representing the directory entry at the current position
in the directory stream specified by the argument dirp, and position the directory stream at
the next entry.
Returns nil when the end is reached or an error occurred (which sets errno).
Example:
posix.set_errno(.NONE)
entry := posix.readdir(dirp)
if entry == nil {
if errno := posix.errno(); errno != .NONE {
panic(string(posix.strerror(errno)))
} else {
fmt.println("end of directory stream")
}
} else {
fmt.println(entry)
}
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/readdir.html ]]
*/
@(link_name=LREADDIR)
readdir :: proc(dirp: DIR) -> ^dirent ---
/*
Reset the position of the directory stream to which dirp refers to the beginning of the directory.
It shall also cause the directory stream to refer to the current state of the corresponding directory,
as a call to opendir() would have done.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/rewinddir.html ]]
*/
@(link_name="rewinddir" + INODE_SUFFIX)
rewinddir :: proc(dirp: DIR) ---
/*
The seekdir() function shall set the position of the next readdir() operation on the directory
stream specified by dirp to the position specified by loc.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/seekdir.html ]]
*/
@(link_name="seekdir" + INODE_SUFFIX)
seekdir :: proc(dirp: DIR, loc: dir_loc) ---
/*
The telldir() function shall obtain the current location associated with the directory stream
specified by dirp.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/telldir.html ]]
*/
@(link_name="telldir" + INODE_SUFFIX)
telldir :: proc(dirp: DIR) -> dir_loc ---
// deprecated.
// readdir_r :: proc(DIR, ^dirent, ^^dirent) -> c.int ---
}
DIR :: distinct rawptr
dir_loc :: c.long
// NOTE: `d_type` is not a POSIX standard field, but all targets we support add it.
D_Type :: enum c.uint8_t {
UNKNOWN = 0,
FIFO = 1,
CHR = 2,
DIR = 4,
BLK = 6,
REG = 8,
LNK = 10,
SOCK = 12,
WHT = 14,
}
when ODIN_OS == .NetBSD {
@(private) LALPHASORT :: "__alphasort30"
@(private) LSCANDIR :: "__scandir30"
@(private) LOPENDIR :: "__opendir30"
@(private) LREADDIR :: "__readdir30"
} else {
@(private) LALPHASORT :: "alphasort" + INODE_SUFFIX
@(private) LSCANDIR :: "scandir" + INODE_SUFFIX
@(private) LOPENDIR :: "opendir" + INODE_SUFFIX
@(private) LREADDIR :: "readdir" + INODE_SUFFIX
}
when ODIN_OS == .Darwin {
dirent :: struct {
d_ino: ino_t, /* [PSX] file number of entry */
d_seekoff: c.uint64_t, /* seek offset */
d_reclen: c.uint16_t, /* length of this record */
d_namelen: c.uint16_t, /* length of string in d_name */
d_type: D_Type, /* file type */
d_name: [1024]c.char `fmt:"s,0"`, /* [PSX] entry name */
}
} else when ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD {
dirent :: struct {
d_ino: ino_t, /* [PSX] file number of entry */
d_off: off_t, /* directory offset of the next entry */
d_reclen: c.uint16_t, /* length of this record */
d_type: D_Type, /* file type */
d_namelen: c.uint8_t, /* length of string in d_name */
d_pad0: c.uint32_t,
d_name: [256]c.char `fmt:"s,0"`, /* [PSX] entry name */
}
} else when ODIN_OS == .NetBSD {
dirent :: struct {
d_ino: ino_t, /* [PSX] file number of entry */
d_reclen: c.uint16_t, /* length of this record */
d_namelen: c.uint16_t, /* length of string in d_name */
d_type: D_Type, /* file type */
d_name: [512]c.char `fmt:"s,0"`, /* [PSX] entry name */
}
} else {
#panic("posix is unimplemented for the current target")
}

117
core/sys/posix/dlfcn.odin Normal file
View File

@@ -0,0 +1,117 @@
package posix
import "core:c"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD {
foreign import lib "system:dl"
} else {
foreign import lib "system:c"
}
// dlfcn.h - dynamic linking
foreign lib {
/*
inform the system that the object referenced by a handle returned from a previous dlopen()
invocation is no longer needed by the application.
Returns: 0 on success, non-zero on failure (use dlerror() for more information)
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/dlclose.html ]]
*/
dlclose :: proc(handle: Symbol_Table) -> c.int ---
/*
return a null-terminated character string (with no trailing <newline>) that describes
the last error that occurred during dynamic linking processing.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/dlerror.html ]]
*/
dlerror :: proc() -> cstring ---
/*
Make the symbols (function identifiers and data object identifiers) in the executable object
file specified by file available to the calling program.
Returns: a reference to the symbol table on success, nil on failure (use dlerror() for more information)
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/dlopen.html ]]
*/
dlopen :: proc(file: cstring, mode: RTLD_Flags) -> Symbol_Table ---
/*
Obtain the address of a symbol (a function identifier or a data object identifier)
defined in the symbol table identified by the handle argument.
Returns: the address of the matched symbol on success, nil on failure (use dlerror() for more information)
Example:
handle := posix.dlopen("/usr/home/me/libfoo.so", posix.RTLD_LOCAL + { .RTLD_LAZY })
defer posix.dlclose(handle)
if handle == nil {
panic(string(posix.dlerror()))
}
foo: proc(a, b: int) -> int
foo = auto_cast posix.dlsym(handle, "foo")
if foo == nil {
panic(string(posix.dlerror()))
}
fmt.printfln("foo(%v, %v) == %v", 1, 2, foo(1, 2))
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/dlsym.html ]]
*/
dlsym :: proc(handle: Symbol_Table, name: cstring) -> rawptr ---
}
RTLD_Flag_Bits :: enum c.int {
LAZY = log2(RTLD_LAZY),
NOW = log2(RTLD_NOW),
GLOBAL = log2(RTLD_GLOBAL),
// NOTE: use with `posix.RTLD_LOCAL + { .OTHER_FLAG, .OTHER_FLAG }`, unfortunately can't be in
// this bit set enum because it is 0 on some platforms and a value on others.
// LOCAL = RTLD_LOCAL
_MAX = 31,
}
RTLD_Flags :: bit_set[RTLD_Flag_Bits; c.int]
Symbol_Table :: distinct rawptr
when ODIN_OS == .Darwin {
RTLD_LAZY :: 0x1
RTLD_NOW :: 0x2
_RTLD_LOCAL :: 0x4
RTLD_GLOBAL :: 0x8
RTLD_LOCAL :: RTLD_Flags{RTLD_Flag_Bits(log2(_RTLD_LOCAL))}
} else when ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD {
RTLD_LAZY :: 1
RTLD_NOW :: 2
_RTLD_LOCAL :: 0
RTLD_GLOBAL :: 0x100
RTLD_LOCAL :: RTLD_Flags{}
} else when ODIN_OS == .NetBSD {
RTLD_LAZY :: 0x1
RTLD_NOW :: 0x2
_RTLD_LOCAL :: 0x200
RTLD_GLOBAL :: 0x100
RTLD_LOCAL :: RTLD_Flags{RTLD_Flag_Bits(log2(_RTLD_LOCAL))}
} else {
#panic("posix is unimplemented for the current target")
}

373
core/sys/posix/errno.odin Normal file
View File

@@ -0,0 +1,373 @@
package posix
import "core:c"
import "core:c/libc"
// errno.h - system error numbers
EDOM :: libc.EDOM
EILSEQ :: libc.EILSEQ
ERANGE :: libc.ERANGE
@(no_instrumentation)
get_errno :: #force_inline proc "contextless" () -> Errno {
return (^Errno)(libc.errno())^
}
set_errno :: #force_inline proc "contextless" (err: Errno) {
libc.errno()^ = i32(err)
}
errno :: proc {
get_errno,
set_errno,
}
Errno :: enum c.int {
NONE = 0,
EDOM = EDOM,
EILSEQ = EILSEQ,
ERANGE = ERANGE,
E2BIG = E2BIG,
EACCES = EACCES,
EADDRINUSE = EADDRINUSE,
EADDRNOTAVAIL = EADDRNOTAVAIL,
EAFNOSUPPORT = EAFNOSUPPORT,
EAGAIN = EAGAIN,
EALREADY = EALREADY,
EBADF = EBADF,
EBADMSG = EBADMSG,
EBUSY = EBUSY,
ECANCELED = ECANCELED,
ECHILD = ECHILD,
ECONNABORTED = ECONNABORTED,
ECONNREFUSED = ECONNREFUSED,
ECONNRESET = ECONNRESET,
EDEADLK = EDEADLK,
EDESTADDRREQ = EDESTADDRREQ,
EDQUOT = EDQUOT,
EEXIST = EEXIST,
EFAULT = EFAULT,
EFBIG = EFBIG,
EHOSTUNREACH = EHOSTUNREACH,
EIDRM = EIDRM,
EINPROGRESS = EINPROGRESS,
EINTR = EINTR,
EINVAL = EINVAL,
EIO = EIO,
EISCONN = EISCONN,
EISDIR = EISDIR,
ELOOP = ELOOP,
EMFILE = EMFILE,
EMLINK = EMLINK,
EMSGSIZE = EMSGSIZE,
EMULTIHOP = EMULTIHOP,
ENAMETOOLONG = ENAMETOOLONG,
ENETDOWN = ENETDOWN,
ENETRESET = ENETRESET,
ENETUNREACH = ENETUNREACH,
ENFILE = ENFILE,
ENOBUFS = ENOBUFS,
ENODATA = ENODATA,
ENODEV = ENODEV,
ENOENT = ENOENT,
ENOEXEC = ENOEXEC,
ENOLCK = ENOLCK,
ENOLINK = ENOLINK,
ENOMEM = ENOMEM,
ENOMSG = ENOMSG,
ENOPROTOOPT = ENOPROTOOPT,
ENOSPC = ENOSPC,
ENOSR = ENOSR,
ENOSTR = ENOSTR,
ENOSYS = ENOSYS,
ENOTCONN = ENOTCONN,
ENOTDIR = ENOTDIR,
ENOTEMPTY = ENOTEMPTY,
ENOTRECOVERABLE = ENOTRECOVERABLE,
ENOTSOCK = ENOTSOCK,
ENOTSUP = ENOTSUP,
ENOTTY = ENOTTY,
ENXIO = ENXIO,
EOPNOTSUPP = EOPNOTSUPP,
EOVERFLOW = EOVERFLOW,
EOWNERDEAD = EOWNERDEAD,
EPERM = EPERM,
EPIPE = EPIPE,
EPROTO = EPROTO,
EPROTONOSUPPORT = EPROTONOSUPPORT,
EPROTOTYPE = EPROTOTYPE,
EROFS = EROFS,
ESPIPE = ESPIPE,
ESRCH = ESRCH,
ESTALE = ESTALE,
ETIME = ETIME,
ETIMEDOUT = ETIMEDOUT,
ETXTBSY = ETXTBSY,
EWOULDBLOCK = EWOULDBLOCK,
EXDEV = EXDEV,
}
when ODIN_OS == .Darwin {
EPERM :: 1
ENOENT :: 2
ESRCH :: 3
EINTR :: 4
EIO :: 5
ENXIO :: 6
E2BIG :: 7
ENOEXEC :: 8
EBADF :: 9
ECHILD :: 10
EDEADLK :: 11
ENOMEM :: 12
EACCES :: 13
EFAULT :: 14
EBUSY :: 16
EEXIST :: 17
EXDEV :: 18
ENODEV :: 19
ENOTDIR :: 20
EISDIR :: 21
EINVAL :: 22
ENFILE :: 23
EMFILE :: 24
ENOTTY :: 25
ETXTBSY :: 26
EFBIG :: 27
ENOSPC :: 28
ESPIPE :: 29
EROFS :: 30
EMLINK :: 31
EPIPE :: 32
EAGAIN :: 35
EWOULDBLOCK :: 35
EINPROGRESS :: 36
EALREADY :: 37
ENOTSOCK :: 38
EDESTADDRREQ :: 39
EMSGSIZE :: 40
EPROTOTYPE :: 41
ENOPROTOOPT :: 42
EPROTONOSUPPORT :: 43
ENOTSUP :: 45
EOPNOTSUPP :: 45
EAFNOSUPPORT :: 47
EADDRINUSE :: 48
EADDRNOTAVAIL :: 49
ENETDOWN :: 50
ENETUNREACH :: 51
ENETRESET :: 52
ECONNABORTED :: 53
ECONNRESET :: 54
ENOBUFS :: 55
EISCONN :: 56
ENOTCONN :: 57
ETIMEDOUT :: 60
ECONNREFUSED :: 61
ELOOP :: 62
ENAMETOOLONG :: 63
EHOSTUNREACH :: 65
ENOTEMPTY :: 66
EDQUOT :: 69
ESTALE :: 70
ENOLCK :: 77
ENOSYS :: 78
EOVERFLOW :: 84
ECANCELED :: 89
EIDRM :: 90
ENOMSG :: 91
EBADMSG :: 94
EMULTIHOP :: 95
ENODATA :: 96
ENOLINK :: 97
ENOSR :: 98
ENOSTR :: 99
EPROTO :: 100
ETIME :: 101
ENOTRECOVERABLE :: 104
EOWNERDEAD :: 105
} else when ODIN_OS == .FreeBSD {
EPERM :: 1
ENOENT :: 2
ESRCH :: 3
EINTR :: 4
EIO :: 5
ENXIO :: 6
E2BIG :: 7
ENOEXEC :: 8
EBADF :: 9
ECHILD :: 10
EDEADLK :: 11
ENOMEM :: 12
EACCES :: 13
EFAULT :: 14
EBUSY :: 16
EEXIST :: 17
EXDEV :: 18
ENODEV :: 19
ENOTDIR :: 20
EISDIR :: 21
EINVAL :: 22
ENFILE :: 23
EMFILE :: 24
ENOTTY :: 25
ETXTBSY :: 26
EFBIG :: 27
ENOSPC :: 28
ESPIPE :: 29
EROFS :: 30
EMLINK :: 31
EPIPE :: 32
EAGAIN :: 35
EWOULDBLOCK :: 35
EINPROGRESS :: 36
EALREADY :: 37
ENOTSOCK :: 38
EDESTADDRREQ :: 39
EMSGSIZE :: 40
EPROTOTYPE :: 41
ENOPROTOOPT :: 42
EPROTONOSUPPORT :: 43
ENOTSUP :: 45
EOPNOTSUPP :: 45
EAFNOSUPPORT :: 47
EADDRINUSE :: 48
EADDRNOTAVAIL :: 49
ENETDOWN :: 50
ENETUNREACH :: 51
ENETRESET :: 52
ECONNABORTED :: 53
ECONNRESET :: 54
ENOBUFS :: 55
EISCONN :: 56
ENOTCONN :: 57
ETIMEDOUT :: 60
ECONNREFUSED :: 61
ELOOP :: 62
ENAMETOOLONG :: 63
EHOSTUNREACH :: 65
ENOTEMPTY :: 66
EDQUOT :: 69
ESTALE :: 70
ENOLCK :: 77
ENOSYS :: 78
EOVERFLOW :: 84
EIDRM :: 82
ENOMSG :: 83
ECANCELED :: 85
EBADMSG :: 89
EMULTIHOP :: 90
ENOLINK :: 91
EPROTO :: 92
ENOTRECOVERABLE :: 95
EOWNERDEAD :: 96
// NOTE: not defined for freebsd
ENODATA :: -1
ENOSR :: -1
ENOSTR :: -1
ETIME :: -1
} else when ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
EPERM :: 1
ENOENT :: 2
ESRCH :: 3
EINTR :: 4
EIO :: 5
ENXIO :: 6
E2BIG :: 7
ENOEXEC :: 8
EBADF :: 9
ECHILD :: 10
EDEADLK :: 11
ENOMEM :: 12
EACCES :: 13
EFAULT :: 14
EBUSY :: 16
EEXIST :: 17
EXDEV :: 18
ENODEV :: 19
ENOTDIR :: 20
EISDIR :: 21
EINVAL :: 22
ENFILE :: 23
EMFILE :: 24
ENOTTY :: 25
ETXTBSY :: 26
EFBIG :: 27
ENOSPC :: 28
ESPIPE :: 29
EROFS :: 30
EMLINK :: 31
EPIPE :: 32
EAGAIN :: 35
EWOULDBLOCK :: 35
EINPROGRESS :: 36
EALREADY :: 37
ENOTSOCK :: 38
EDESTADDRREQ :: 39
EMSGSIZE :: 40
EPROTOTYPE :: 41
ENOPROTOOPT :: 42
EPROTONOSUPPORT :: 43
ENOTSUP :: 45
EOPNOTSUPP :: 45
EAFNOSUPPORT :: 47
EADDRINUSE :: 48
EADDRNOTAVAIL :: 49
ENETDOWN :: 50
ENETUNREACH :: 51
ENETRESET :: 52
ECONNABORTED :: 53
ECONNRESET :: 54
ENOBUFS :: 55
EISCONN :: 56
ENOTCONN :: 57
ETIMEDOUT :: 60
ECONNREFUSED :: 61
ELOOP :: 62
ENAMETOOLONG :: 63
EHOSTUNREACH :: 65
ENOTEMPTY :: 66
EDQUOT :: 69
ESTALE :: 70
ENOLCK :: 77
ENOSYS :: 78
when ODIN_OS == .NetBSD {
EOVERFLOW :: 84
EIDRM :: 82
ENOMSG :: 83
ECANCELED :: 87
EBADMSG :: 88
ENODATA :: 89
EMULTIHOP :: 94
ENOLINK :: 95
EPROTO :: 96
ENOTRECOVERABLE :: 98
EOWNERDEAD :: 97
ENOSR :: 90
ENOSTR :: 91
ETIME :: 92
} else {
EOVERFLOW :: 87
EIDRM :: 89
ENOMSG :: 90
ECANCELED :: 88
EBADMSG :: 92
EPROTO :: 95
ENOTRECOVERABLE :: 93
EOWNERDEAD :: 94
// NOTE: not defined for openbsd
ENODATA :: -1
EMULTIHOP :: -1
ENOLINK :: -1
ENOSR :: -1
ENOSTR :: -1
ETIME :: -1
}
} else {
#panic("posix is unimplemented for the current target")
}

415
core/sys/posix/fcntl.odin Normal file
View File

@@ -0,0 +1,415 @@
package posix
import "core:c"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// fcntl.h - file control options
foreign lib {
/*
Implemented as `return open(path, O_WRONLY|O_CREAT|O_TRUNC, mode);`
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/creat.html ]]
*/
creat :: proc(path: cstring, mode: mode_t) -> FD ---
/*
Perform the operations on open files.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fcntl.html ]]
*/
fcntl :: proc(fd: FD, cmd: FCNTL_Cmd, #c_vararg args: ..any) -> c.int ---
/*
Establish the connection between a file and a file descriptor.
It shall create an open file description that refers to a file and a file descriptor that
refers to that open file description. The file descriptor is used by other I/O functions to
refer to that file.
The path argument points to a pathname naming the file
Returns: -1 on failure (setting errno), a file descriptor on success.
Example:
// The following example opens the file /tmp/file, either by creating it (if it does not already exist),
// or by truncating its length to 0 (if it does exist). In the former case, if the call creates a new file,
// the access permission bits in the file mode of the file are set to permit reading and writing by the owner,
// and to permit reading only by group members and others.
fd := posix.open("/tmp/file", { .WRONLY, .CREAT, .TRUNC }, { .IRUSR, .IWUSR, .IRGRP, .IROTH })
// The following example uses the open() function to try to create the LOCKFILE file and open it for writing.
// Since the open() function specifies the O_EXCL flag, the call fails if the file already exists.
// In that case, the program assumes that someone else is updating the password file and exits.
fd := posix.open("/etc/ptmp", { .WRONLY, .CREAT, .EXCL }, { .IRUSR, .IWUSR, .IRGRP, .IROTH })
if fd == -1 {
fmt.println("cannot open /etc/ptmp")
}
// The following example opens a file for writing, creating the file if it does not already exist.
// If the file does exist, the system truncates the file to zero bytes.
fd := posix.open("/etc/ptmp", { .WRONLY, .CREAT, .TRUNC }, { .IRUSR, .IWUSR, .IRGRP, .IROTH })
if fd == -1 {
fmt.println("cannot open output file")
}
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html ]]
*/
open :: proc(path: cstring, flags: O_Flags, mode: mode_t = {}) -> FD ---
/*
Equivalent to the open() function except in the case where path specifies a relative path.
In this case the file to be opened is determined relative to the directory associated with the
file descriptor fd instead of the current working directory.
Returns: -1 on failure (setting errno), a file descriptor on success.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html ]]
*/
openat :: proc(fd: FD, path: cstring, flags: O_Flags, mode: mode_t = {}) -> FD ---
}
FCNTL_Cmd :: enum c.int {
DUPFD = F_DUPFD,
DUPFD_CLOEXEC = F_DUPFD_CLOEXEC,
GETFD = F_GETFD,
SETFD = F_SETFD,
GETFL = F_GETFL,
SETFL = F_SETFL,
GETLK = F_GETLK,
SETLK = F_SETLK,
SETLKW = F_SETLKW,
GETOWN = F_GETOWN,
SETOWN = F_SETOWN,
}
Lock_Type :: enum c.short {
RDLCK = F_RDLCK,
UNLCK = F_UNLCK,
WRLCK = F_WRLCK,
}
// Assertions made to unify this bit set.
#assert(O_RDONLY == 0)
O_Flag_Bits :: enum c.int {
// Sets FD_CLOEXEC on the file descriptor.
CLOEXEC = log2(O_CLOEXEC),
// If not exists, combined with DIRECTORY will cause creation of a directory, otherwise a regular file.
CREAT = log2(O_CREAT),
// Fails if the opened descriptor would not be a directory.
DIRECTORY = log2(O_DIRECTORY),
// If combined with CREAT, causes a failure if the file already exists.
EXCL = log2(O_EXCL),
// If terminal device, do not make it the controlling terminal for the process.
NOCTTY = log2(O_NOCTTY),
// Don't follow symbolic links, fail with errno ELOOP.
NOFOLLOW = log2(O_NOFOLOW),
// If exists and regular, truncate the length to 0.
TRUNC = log2(O_TRUNC),
// NOTE: use with `posix.O_TTY_INIT + { .OTHER_FLAG, .OTHER_FLAG }`, unfortunately can't be in
// this bit set enum because it is 0 on some platforms and a value on others.
// TTY_INIT = O_TTY_INIT,
// Set file offset to end of file prior to each write.
APPEND = log2(O_APPEND),
// Write I/O shall complete as defined by synchronized I/O data integrity completion.
DSYNC = log2(O_DSYNC),
// Causes nonblocking behaviour in various situations.
NONBLOCK = log2(O_NONBLOCK),
// Write I/O shall complete as defined by synchronized I/O file integrity completion.
SYNC = log2(O_SYNC),
// NOTE: use with `posix.O_RSYNC + { .OTHER_FLAG, .OTHER_FLAG }`, unfortunately can't be in
// this bit set enum because it is 0 on some platforms and a value on others.
// RSYNC = O_RSYNC,
// Execute only.
EXEC = log2(O_EXEC),
// Reading and writing.
RDWR = log2(O_RDWR),
// Writing only.
WRONLY = log2(O_WRONLY),
// Reading only.
// RDONLY = 0, // Default
}
O_Flags :: bit_set[O_Flag_Bits; c.int]
// A mask of all the access mode bits.
O_ACCMODE :: O_Flags{ .EXEC, .RDWR, .WRONLY }
AT_Flag_Bits :: enum c.int {
EACCESS = log2(AT_EACCESS),
SYMLINK_NOFOLLOW = log2(AT_SYMLINK_NOFOLLOW),
SYMLINK_FOLLOW = log2(AT_SYMLINK_FOLLOW),
REMOVEDIR = log2(AT_REMOVEDIR),
}
AT_Flags :: bit_set[AT_Flag_Bits; c.int]
when ODIN_OS == .Darwin {
off_t :: distinct c.int64_t
pid_t :: distinct c.int32_t
F_DUPFD :: 0
F_DUPFD_CLOEXEC :: 67
F_GETFD :: 1
F_SETFD :: 2
F_GETFL :: 3
F_SETFL :: 4
F_GETLK :: 7
F_SETLK :: 8
F_SETLKW :: 9
F_GETOWN :: 5
F_SETOWN :: 6
FD_CLOEXEC :: 1
F_RDLCK :: 1
F_UNLCK :: 2
F_WRLCK :: 3
O_CLOEXEC :: 0x01000000
O_CREAT :: 0x00000200
O_DIRECTORY :: 0x00100000
O_EXCL :: 0x00000800
O_NOCTTY :: 0x00020000
O_NOFOLOW :: 0x00000100
O_TRUNC :: 0x00000400
_O_TTY_INIT :: 0
O_TTY_INIT :: O_Flags{}
O_APPEND :: 0x00000008
O_DSYNC :: 0x00400000
O_NONBLOCK :: 0x00000004
O_SYNC :: 0x0080
_O_RSYNC :: 0
O_RSYNC :: O_Flags{}
O_EXEC :: 0x40000000
O_RDONLY :: 0
O_RDWR :: 0x0002
O_WRONLY :: 0x0001
_O_SEARCH :: O_EXEC | O_DIRECTORY
O_SEARCH :: O_Flags{ .EXEC, .DIRECTORY }
AT_FDCWD: FD: -2
AT_EACCESS :: 0x0010
AT_SYMLINK_NOFOLLOW :: 0x0020
AT_SYMLINK_FOLLOW :: 0x0040
AT_REMOVEDIR :: 0x0080
flock :: struct {
l_start: off_t, /* [PSX] relative offset in bytes */
l_len: off_t, /* [PSX] size; if 0 then until EOF */
l_pid: pid_t, /* [PSX] process ID of the process holding the lock */
l_type: Lock_Type, /* [PSX] type of lock */
l_whence: c.short, /* [PSX] flag (Whence) of starting offset */
}
} else when ODIN_OS == .FreeBSD {
off_t :: distinct c.int64_t
pid_t :: distinct c.int32_t
F_DUPFD :: 0
F_DUPFD_CLOEXEC :: 17
F_GETFD :: 1
F_SETFD :: 2
F_GETFL :: 3
F_SETFL :: 4
F_GETLK :: 7
F_SETLK :: 8
F_SETLKW :: 9
F_GETOWN :: 5
F_SETOWN :: 6
FD_CLOEXEC :: 1
F_RDLCK :: 1
F_UNLCK :: 2
F_WRLCK :: 3
O_CLOEXEC :: 0x00100000
O_CREAT :: 0x0200
O_DIRECTORY :: 0x00020000
O_EXCL :: 0x0800
O_NOCTTY :: 0x8000
O_NOFOLOW :: 0x0100
O_TRUNC :: 0x0400
_O_TTY_INIT :: 0x00080000
O_TTY_INIT :: O_Flags{O_Flag_Bits(log2(_O_TTY_INIT))}
O_APPEND :: 0x0008
O_DSYNC :: 0x01000000
O_NONBLOCK :: 0x0004
O_SYNC :: 0x0080
_O_RSYNC :: 0
O_RSYNC :: O_Flags{} // NOTE: not defined in headers
O_EXEC :: 0x00040000
O_RDONLY :: 0
O_RDWR :: 0x0002
O_WRONLY :: 0x0001
_O_SEARCH :: O_EXEC
O_SEARCH :: O_Flags{ .EXEC }
AT_FDCWD: FD: -100
AT_EACCESS :: 0x0100
AT_SYMLINK_NOFOLLOW :: 0x0200
AT_SYMLINK_FOLLOW :: 0x0400
AT_REMOVEDIR :: 0x0800
flock :: struct {
l_start: off_t, /* [PSX] relative offset in bytes */
l_len: off_t, /* [PSX] size; if 0 then until EOF */
l_pid: pid_t, /* [PSX] process ID of the process holding the lock */
l_type: Lock_Type, /* [PSX] type of lock */
l_whence: c.short, /* [PSX] flag (Whence) of starting offset */
l_sysid: c.int,
}
} else when ODIN_OS == .NetBSD {
off_t :: distinct c.int64_t
pid_t :: distinct c.int32_t
F_DUPFD :: 0
F_DUPFD_CLOEXEC :: 12
F_GETFD :: 1
F_SETFD :: 2
F_GETFL :: 3
F_SETFL :: 4
F_GETLK :: 7
F_SETLK :: 8
F_SETLKW :: 9
F_GETOWN :: 5
F_SETOWN :: 6
FD_CLOEXEC :: 1
F_RDLCK :: 1
F_UNLCK :: 2
F_WRLCK :: 3
O_CLOEXEC :: 0x00400000
O_CREAT :: 0x0200
O_DIRECTORY :: 0x0020000
O_EXCL :: 0x0800
O_NOCTTY :: 0x8000
O_NOFOLOW :: 0x0100
O_TRUNC :: 0x0400
_O_TTY_INIT :: 0
O_TTY_INIT :: O_Flags{} // NOTE: not defined in the headers
O_APPEND :: 0x0008
O_DSYNC :: 0x010000
O_NONBLOCK :: 0x0004
O_SYNC :: 0x0080
_O_RSYNC :: 0x0002
O_RSYNC :: O_Flags{O_Flag_Bits(log2(_O_RSYNC))}
O_EXEC :: 0x04000000
O_RDONLY :: 0
O_RDWR :: 0x0002
O_WRONLY :: 0x0001
_O_SEARCH :: 0x00800000
O_SEARCH :: O_Flags{O_Flag_Bits(log2(_O_SEARCH))}
AT_FDCWD: FD: -100
AT_EACCESS :: 0x100
AT_SYMLINK_NOFOLLOW :: 0x200
AT_SYMLINK_FOLLOW :: 0x400
AT_REMOVEDIR :: 0x800
flock :: struct {
l_start: off_t, /* [PSX] relative offset in bytes */
l_len: off_t, /* [PSX] size; if 0 then until EOF */
l_pid: pid_t, /* [PSX] process ID of the process holding the lock */
l_type: Lock_Type, /* [PSX] type of lock */
l_whence: c.short, /* [PSX] flag (Whence) of starting offset */
}
} else when ODIN_OS == .OpenBSD {
off_t :: distinct c.int64_t
pid_t :: distinct c.int32_t
F_DUPFD :: 0
F_DUPFD_CLOEXEC :: 10
F_GETFD :: 1
F_SETFD :: 2
F_GETFL :: 3
F_SETFL :: 4
F_GETLK :: 7
F_SETLK :: 8
F_SETLKW :: 9
F_GETOWN :: 5
F_SETOWN :: 6
FD_CLOEXEC :: 1
F_RDLCK :: 1
F_UNLCK :: 2
F_WRLCK :: 3
O_CLOEXEC :: 0x10000
O_CREAT :: 0x0200
O_DIRECTORY :: 0x20000
O_EXCL :: 0x0800
O_NOCTTY :: 0x8000
O_NOFOLOW :: 0x0100
O_TRUNC :: 0x0400
_O_TTY_INIT :: 0
O_TTY_INIT :: O_Flags{} // NOTE: not defined in the headers
O_APPEND :: 0x0008
O_DSYNC :: 0x010000
O_NONBLOCK :: 0x0004
O_SYNC :: 0x0080
_O_RSYNC :: O_SYNC
O_RSYNC :: O_Flags{ .SYNC }
O_EXEC :: 0x04000000 // NOTE: not defined in the headers
O_RDONLY :: 0
O_RDWR :: 0x0002
O_WRONLY :: 0x0001
_O_SEARCH :: 0
O_SEARCH :: O_Flags{} // NOTE: not defined in the headers
AT_FDCWD: FD: -100
AT_EACCESS :: 0x01
AT_SYMLINK_NOFOLLOW :: 0x02
AT_SYMLINK_FOLLOW :: 0x04
AT_REMOVEDIR :: 0x08
flock :: struct {
l_start: off_t, /* [PSX] relative offset in bytes */
l_len: off_t, /* [PSX] size; if 0 then until EOF */
l_pid: pid_t, /* [PSX] process ID of the process holding the lock */
l_type: Lock_Type, /* [PSX] type of lock */
l_whence: c.short, /* [PSX] flag (Whence) of starting offset */
}
} else {
#panic("posix is unimplemented for the current target")
}

View File

@@ -0,0 +1,58 @@
package posix
import "core:c"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// fnmatch.h - filename-matching types
foreign lib {
/*
Match patterns as described in XCU [[ Patterns Matching a Single Character; https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13_01 ]]
// and [[ Patterns Matching Multiple Characters; https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13_02 ]].
It checks the string specified by the string argument to see if it matches the pattern specified by the pattern argument.
Returns: 0 when matched. if there is no match, fnmatch() shall return FNM_NOMATCH. Non-zero on other errors.
Example:
assert(posix.fnmatch("*.odin", "foo.odin", {}) == 0)
assert(posix.fnmatch("*.txt", "foo.odin", {}) == posix.FNM_NOMATCH)
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fnmatch.html ]]
*/
fnmatch :: proc(pattern: cstring, string: cstring, flags: FNM_Flags) -> c.int ---
}
FNM_Flag_Bits :: enum c.int {
// A <slash> character ( '/' ) in string shall be explicitly matched by a <slash> in pattern;
// it shall not be matched by either the <asterisk> or <question-mark> special characters,
// nor by a bracket expression.
PATHNAME = log2(FNM_PATHNAME),
// A leading <period> ( '.' ) in string shall match a <period> in pattern;
// as described by rule 2 in XCU [[ Patterns Used for Filename Expansion; https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13_03 ]]
// where the location of "leading" is indicated by the value of PATHNAME:
// 1. If PATHNAME is set, a <period> is "leading" if it is the first character in string or if it immediately follows a <slash>.
// 2. If PATHNAME is not set, a <period> is "leading" only if it is the first character of string.
PERIOD = log2(FNM_PERIOD),
// A <backslash> character shall be treated as an ordinary character.
NOESCAPE = log2(FNM_NOESCAPE),
}
FNM_Flags :: bit_set[FNM_Flag_Bits; c.int]
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
FNM_NOMATCH :: 1
FNM_PATHNAME :: 0x02
FNM_PERIOD :: 0x04
FNM_NOESCAPE :: 0x01
} else {
#panic("posix is unimplemented for the current target")
}

179
core/sys/posix/glob.odin Normal file
View File

@@ -0,0 +1,179 @@
package posix
import "core:c"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// glob.h - pathname pattern-matching types
foreign lib {
/*
The glob() function is a pathname generator that shall implement the rules defined in
[[ XCU Pattern Matching Notation; https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13 ]],
with optional support for rule 3 in XCU [[ Patterns Used for Filename Expansion; https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13_03 ]].
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/glob.html ]]
*/
@(link_name=LGLOB)
glob :: proc(
pattern: cstring,
flags: Glob_Flags,
errfunc: proc "c" (epath: cstring, eerrno: Errno) -> b32 = nil, // Return `true` to abort the glob().
pglob: ^glob_t,
) -> Glob_Result ---
/*
Free the glob results.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/glob.html ]]
*/
@(link_name=LGLOBFREE)
globfree :: proc(^glob_t) ---
}
Glob_Flag_Bits :: enum c.int {
// Append pathnames generated to the ones from a previous call to glob().
APPEND = log2(GLOB_APPEND),
// Make use of pglob->gl_offs. If this flag is set, pglob->gl_offs is used to specify how many null pointers to add to the beginning of pglob->gl_pathv.
// In other words, pglob->gl_pathv shall point to pglob->gl_offs null pointers, followed by pglob->gl_pathc pathname pointers, followed by a null pointer.
DOOFFS = log2(GLOB_DOOFFS),
// Cause glob() to return when it encounters a directory that it cannot open or read. Ordinarily,
// glob() continues to find matches.
ERR = log2(GLOB_ERR),
// Each pathname that is a directory that matches pattern shall have a <slash> appended.
MARK = log2(GLOB_MARK),
// Supports rule 3 in [[ XCU Patterns Used for Filename Expansion; https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13_03 ]].
// If pattern does not match any pathname, then glob() shall return a list consisting of only pattern,
// and the number of matched pathnames is 1.
NOCHECK = log2(GLOB_NOCHECK),
// Disable backslash escaping.
NOESCAPE = log2(GLOB_NOESCAPE),
// Ordinarily, glob() sorts the matching pathnames according to the current setting of the
// LC_COLLATE category; see XBD LC_COLLATE. When this flag is used,
// the order of pathnames returned is unspecified.
NOSORT = log2(GLOB_NOSORT),
}
Glob_Flags :: bit_set[Glob_Flag_Bits; c.int]
Glob_Result :: enum c.int {
SUCCESS = 0,
ABORTED = GLOB_ABORTED,
NOMATCH = GLOB_NOMATCH,
NOSPACE = GLOB_NOSPACE,
}
when ODIN_OS == .NetBSD {
@(private) LGLOB :: "__glob30"
@(private) LGLOBFREE :: "__globfree30"
} else {
@(private) LGLOB :: "glob" + INODE_SUFFIX
@(private) LGLOBFREE :: "globfree"
}
when ODIN_OS == .Darwin {
glob_t :: struct {
gl_pathc: c.size_t, /* [PSX] count of paths matched by pattern */
gl_matchc: c.int, /* count of paths matching pattern */
gl_offs: c.size_t, /* [PSX] slots to reserve at the beginning of gl_pathv */
gl_flags: Glob_Flags, /* copy of flags parameter to glob */
gl_pathv: [^]cstring `fmt:"v,gl_pathc"`, /* [PSX] pointer to list of matched pathnames */
// Non-standard alternate file system access functions:
using _: struct #raw_union {
gl_errfunc: proc "c" (cstring, c.int) -> c.int,
gl_errblk: proc "c" (cstring, c.int) -> c.int,
},
gl_closedir: proc "c" (dirp: DIR),
gl_readdir: proc "c" (dirp: DIR) -> ^dirent,
gl_opendir: proc "c" (path: cstring) -> DIR,
gl_lstat: proc "c" (path: cstring, buf: ^stat_t) -> result,
gl_stat: proc "c" (path: cstring, buf: ^stat_t) -> result,
}
GLOB_APPEND :: 0x0001
GLOB_DOOFFS :: 0x0002
GLOB_ERR :: 0x0004
GLOB_MARK :: 0x0008
GLOB_NOCHECK :: 0x0010
GLOB_NOESCAPE :: 0x2000
GLOB_NOSORT :: 0x0020
GLOB_ABORTED :: -2
GLOB_NOMATCH :: -3
GLOB_NOSPACE :: -1
} else when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD {
glob_t :: struct {
gl_pathc: c.size_t, /* [PSX] count of paths matched by pattern */
gl_matchc: c.size_t, /* count of paths matching pattern */
gl_offs: c.size_t, /* [PSX] slots to reserve at the beginning of gl_pathv */
gl_flags: Glob_Flags, /* copy of flags parameter to glob */
gl_pathv: [^]cstring `fmt:"v,gl_pathc"`, /* [PSX] pointer to list of matched pathnames */
// Non-standard alternate file system access functions:
gl_errfunc: proc "c" (cstring, c.int) -> c.int,
gl_closedir: proc "c" (dirp: DIR),
gl_readdir: proc "c" (dirp: DIR) -> ^dirent,
gl_opendir: proc "c" (path: cstring) -> DIR,
gl_lstat: proc "c" (path: cstring, buf: ^stat_t) -> result,
gl_stat: proc "c" (path: cstring, buf: ^stat_t) -> result,
}
GLOB_APPEND :: 0x0001
GLOB_DOOFFS :: 0x0002
GLOB_ERR :: 0x0004
GLOB_MARK :: 0x0008
GLOB_NOCHECK :: 0x0010
GLOB_NOESCAPE :: 0x2000 when ODIN_OS == .FreeBSD else 0x0100
GLOB_NOSORT :: 0x0020
GLOB_ABORTED :: -2
GLOB_NOMATCH :: -3
GLOB_NOSPACE :: -1
} else when ODIN_OS == .OpenBSD {
glob_t :: struct {
gl_pathc: c.size_t, /* [PSX] count of paths matched by pattern */
gl_matchc: c.size_t, /* count of paths matching pattern */
gl_offs: c.size_t, /* [PSX] slots to reserve at the beginning of gl_pathv */
gl_flags: Glob_Flags, /* copy of flags parameter to glob */
gl_pathv: [^]cstring `fmt:"v,gl_pathc"`, /* [PSX] pointer to list of matched pathnames */
gl_statv: [^]stat_t,
// Non-standard alternate file system access functions:
gl_errfunc: proc "c" (cstring, c.int) -> c.int,
gl_closedir: proc "c" (dirp: DIR),
gl_readdir: proc "c" (dirp: DIR) -> ^dirent,
gl_opendir: proc "c" (path: cstring) -> DIR,
gl_lstat: proc "c" (path: cstring, buf: ^stat_t) -> result,
gl_stat: proc "c" (path: cstring, buf: ^stat_t) -> result,
}
GLOB_APPEND :: 0x0001
GLOB_DOOFFS :: 0x0002
GLOB_ERR :: 0x0004
GLOB_MARK :: 0x0008
GLOB_NOCHECK :: 0x0010
GLOB_NOESCAPE :: 0x1000
GLOB_NOSORT :: 0x0020
GLOB_ABORTED :: -2
GLOB_NOMATCH :: -3
GLOB_NOSPACE :: -1
} else {
#panic("posix is unimplemented for the current target")
}

130
core/sys/posix/grp.odin Normal file
View File

@@ -0,0 +1,130 @@
package posix
import "core:c"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// grp.h - group structure
foreign lib {
/*
Closes the group database.
Checking status would be done by setting errno to 0, calling this, and checking errno.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/endgrent.html ]]
*/
endgrent :: proc() ---
/*
Rewinds the group database so getgrent() returns the first entry again.
Checking status would be done by setting errno to 0, calling this, and checking errno.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/endgrent.html ]]
*/
setgrent :: proc() ---
/*
Returns a pointer to an entry of the group database.
Opens the group database if it isn't.
Returns: nil on failure (setting errno) or EOF (not setting errno), the entry otherwise
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/endgrent.html ]]
*/
getgrent :: proc() -> ^group ---
/*
Searches for an entry with a matching gid in the group database.
Returns: nil (setting errno) on failure, a pointer to the entry on success
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getgrgid.html ]]
*/
getgrgid :: proc(gid: gid_t) -> ^group ---
/*
Searches for an entry with a matching gid in the group database.
Updates grp with the matching entry and stores it (or a nil pointer (setting errno)) into result.
Strings are allocated into the given buffer, you can call `sysconf(._GETGR_R_SIZE_MAX)` for an appropriate size.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getgrgid.html ]]
*/
getgrgid_r :: proc(gid: gid_t, grp: ^group, buffer: [^]byte, bufsize: c.size_t, result: ^^group) -> Errno ---
/*
Searches for an entry with a matching gid in the group database.
Returns: nil (setting errno) on failure, a pointer to the entry on success
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getgrnam.html ]]
*/
getgrnam :: proc(name: cstring) -> ^group ---
/*
Searches for an entry with a matching gid in the group database.
Updates grp with the matching entry and stores it (or a nil pointer (setting errno)) into result.
Strings are allocated into the given buffer, you can call `sysconf(._GETGR_R_SIZE_MAX)` for an appropriate size.
Example:
length := posix.sysconf(._GETGR_R_SIZE_MAX)
if length == -1 {
length = 1024
}
result: posix.group
resultp: ^posix.group
e: posix.Errno
buffer: [dynamic]byte
defer delete(buffer)
for {
mem_err := resize(&buffer, length)
assert(mem_err == nil)
e = posix.getgrnam_r("nobody", &result, raw_data(buffer), len(buffer), &resultp)
if e != .ERANGE {
break
}
length *= 2
assert(length > 0)
}
if e != .NONE {
panic(string(posix.strerror(e)))
}
fmt.println(result)
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getgrnam.html ]]
*/
getgrnam_r :: proc(name: cstring, grp: ^group, buffer: [^]byte, bufsize: c.size_t, result: ^^group) -> Errno ---
}
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
gid_t :: distinct c.uint32_t
group :: struct {
gr_name: cstring, /* [PSX] group name */
gr_passwd: cstring, /* group password */
gr_gid: gid_t, /* [PSX] group id */
gr_mem: [^]cstring, /* [PSX] group members */
}
} else {
#panic("posix is unimplemented for the current target")
}

50
core/sys/posix/iconv.odin Normal file
View File

@@ -0,0 +1,50 @@
package posix
import "core:c"
when ODIN_OS == .Darwin {
// NOTE: iconv is in a different library
foreign import lib "system:iconv"
} else {
foreign import lib "system:c"
}
// iconv.h - codeset conversion facility
iconv_t :: distinct rawptr
foreign lib {
/*
Convert the sequence of characters from one codeset, in the array specified by inbuf,
into a sequence of corresponding characters in another codeset, in the array specified by outbuf.
Returns: -1 (setting errno) on failure, the number of non-identical conversions performed on success
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/iconv.html ]]
*/
iconv :: proc(
cd: iconv_t,
inbuf: ^[^]byte,
inbytesleft: ^c.size_t,
outbuf: ^[^]byte,
outbyteslen: ^c.size_t,
) -> c.size_t ---
/*
Deallocates the conversion descriptor cd and all other associated resources allocated by iconv_open().
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/iconv_close.html ]]
*/
iconv_close :: proc(cd: iconv_t) -> result ---
/*
Returns a conversion descriptor that describes a conversion from the codeset specified by the
string pointed to by the fromcode argument to the codeset specified by the string pointed to by
the tocode argument.
Returns: -1 (setting errno) on failure, a conversion descriptor on success
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/iconv_open.html ]]
*/
iconv_open :: proc(tocode: cstring, fromcode: cstring) -> iconv_t ---
}

View File

@@ -0,0 +1,285 @@
package posix
import "core:c"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// langinfo.h - language information constants
foreign lib {
/*
Return a pointer to a string containing information relevant to the particular language or
cultural area defined in the current locale.
Returns: a string that should not be freed or modified, and that can be invalidated at any time later
Example:
for item in posix.nl_item {
fmt.printfln("%v: %q", item, posix.nl_langinfo(item))
}
Possible Output:
CODESET: "US-ASCII"
D_T_FMT: "%a %b %e %H:%M:%S %Y"
D_FMT: "%m/%d/%y"
T_FMT: "%H:%M:%S"
T_FMT_AMPM: "%I:%M:%S %p"
AM_STR: "AM"
PM_STR: "PM"
DAY_1: "Sunday"
DAY_2: "Monday"
DAY_3: "Tuesday"
DAY_4: "Wednesday"
DAY_5: "Thursday"
DAY_6: "Friday"
DAY_7: "Saturday"
ABDAY_1: "Sun"
ABDAY_2: "Mon"
ABDAY_3: "Tue"
ABDAY_4: "Wed"
ABDAY_5: "Thu"
ABDAY_6: "Fri"
ABDAY_7: "Sat"
MON_1: "January"
MON_2: "February"
MON_3: "March"
MON_4: "April"
MON_5: "May"
MON_6: "June"
MON_7: "July"
MON_8: "August"
MON_9: "September"
MON_10: "October"
MON_11: "November"
MON_12: "December"
ABMON_1: "Jan"
ABMON_2: "Feb"
ABMON_3: "Mar"
ABMON_4: "Apr"
ABMON_5: "May"
ABMON_6: "Jun"
ABMON_7: "Jul"
ABMON_8: "Aug"
ABMON_9: "Sep"
ABMON_10: "Oct"
ABMON_11: "Nov"
ABMON_12: "Dec"
ERA: ""
ERA_D_FMT: ""
ERA_D_T_FMT: ""
ERA_T_FMT: ""
ALT_DIGITS: ""
RADIXCHAR: "."
THOUSEP: ""
YESEXPR: "^[yY]"
NOEXPR: "^[nN]"
CRNCYSTR: ""
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/nl_langinfo.html ]]
*/
nl_langinfo :: proc(nl_item) -> cstring ---
}
nl_item :: enum nl_item_t {
CODESET = CODESET,
D_T_FMT = D_T_FMT,
D_FMT = D_FMT,
T_FMT = T_FMT,
T_FMT_AMPM = T_FMT_AMPM,
AM_STR = AM_STR,
PM_STR = PM_STR,
DAY_1 = DAY_1,
DAY_2 = DAY_2,
DAY_3 = DAY_3,
DAY_4 = DAY_4,
DAY_5 = DAY_5,
DAY_6 = DAY_6,
DAY_7 = DAY_7,
ABDAY_1 = ABDAY_1,
ABDAY_2 = ABDAY_2,
ABDAY_3 = ABDAY_3,
ABDAY_4 = ABDAY_4,
ABDAY_5 = ABDAY_5,
ABDAY_6 = ABDAY_6,
ABDAY_7 = ABDAY_7,
MON_1 = MON_1,
MON_2 = MON_2,
MON_3 = MON_3,
MON_4 = MON_4,
MON_5 = MON_5,
MON_6 = MON_6,
MON_7 = MON_7,
MON_8 = MON_8,
MON_9 = MON_9,
MON_10 = MON_10,
MON_11 = MON_11,
MON_12 = MON_12,
ABMON_1 = ABMON_1,
ABMON_2 = ABMON_2,
ABMON_3 = ABMON_3,
ABMON_4 = ABMON_4,
ABMON_5 = ABMON_5,
ABMON_6 = ABMON_6,
ABMON_7 = ABMON_7,
ABMON_8 = ABMON_8,
ABMON_9 = ABMON_9,
ABMON_10 = ABMON_10,
ABMON_11 = ABMON_11,
ABMON_12 = ABMON_12,
ERA = ERA,
ERA_D_FMT = ERA_D_FMT,
ERA_D_T_FMT = ERA_D_T_FMT,
ERA_T_FMT = ERA_T_FMT,
ALT_DIGITS = ALT_DIGITS,
RADIXCHAR = RADIXCHAR,
THOUSEP = THOUSEP,
YESEXPR = YESEXPR,
NOEXPR = NOEXPR,
CRNCYSTR = CRNCYSTR,
}
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD {
// NOTE: declared with `_t` so we can enumerate the real `nl_info`.
nl_item_t :: distinct c.int
CODESET :: 0
D_T_FMT :: 1
D_FMT :: 2
T_FMT :: 3
T_FMT_AMPM :: 4
AM_STR :: 5
PM_STR :: 6
DAY_1 :: 7
DAY_2 :: 8
DAY_3 :: 9
DAY_4 :: 10
DAY_5 :: 11
DAY_6 :: 12
DAY_7 :: 13
ABDAY_1 :: 14
ABDAY_2 :: 15
ABDAY_3 :: 16
ABDAY_4 :: 17
ABDAY_5 :: 18
ABDAY_6 :: 19
ABDAY_7 :: 20
MON_1 :: 21
MON_2 :: 22
MON_3 :: 23
MON_4 :: 24
MON_5 :: 25
MON_6 :: 26
MON_7 :: 27
MON_8 :: 28
MON_9 :: 29
MON_10 :: 30
MON_11 :: 31
MON_12 :: 32
ABMON_1 :: 33
ABMON_2 :: 34
ABMON_3 :: 35
ABMON_4 :: 36
ABMON_5 :: 37
ABMON_6 :: 38
ABMON_7 :: 39
ABMON_8 :: 40
ABMON_9 :: 41
ABMON_10 :: 42
ABMON_11 :: 43
ABMON_12 :: 44
ERA :: 45
ERA_D_FMT :: 46
ERA_D_T_FMT :: 47
ERA_T_FMT :: 48
ALT_DIGITS :: 49
RADIXCHAR :: 50
THOUSEP :: 51
YESEXPR :: 52
NOEXPR :: 53
CRNCYSTR :: 56
} else when ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
// NOTE: declared with `_t` so we can enumerate the real `nl_info`.
nl_item_t :: distinct c.int
CODESET :: 51
D_T_FMT :: 0
D_FMT :: 1
T_FMT :: 2
T_FMT_AMPM :: 3
AM_STR :: 4
PM_STR :: 5
DAY_1 :: 6
DAY_2 :: 7
DAY_3 :: 8
DAY_4 :: 9
DAY_5 :: 10
DAY_6 :: 11
DAY_7 :: 12
ABDAY_1 :: 13
ABDAY_2 :: 14
ABDAY_3 :: 15
ABDAY_4 :: 16
ABDAY_5 :: 17
ABDAY_6 :: 18
ABDAY_7 :: 19
MON_1 :: 20
MON_2 :: 21
MON_3 :: 22
MON_4 :: 23
MON_5 :: 24
MON_6 :: 25
MON_7 :: 26
MON_8 :: 27
MON_9 :: 28
MON_10 :: 29
MON_11 :: 30
MON_12 :: 31
ABMON_1 :: 32
ABMON_2 :: 33
ABMON_3 :: 34
ABMON_4 :: 35
ABMON_5 :: 36
ABMON_6 :: 37
ABMON_7 :: 38
ABMON_8 :: 39
ABMON_9 :: 40
ABMON_10 :: 41
ABMON_11 :: 42
ABMON_12 :: 43
ERA :: 52
ERA_D_FMT :: 53
ERA_D_T_FMT :: 54
ERA_T_FMT :: 55
ALT_DIGITS :: 56
RADIXCHAR :: 44
THOUSEP :: 45
YESEXPR :: 47
NOEXPR :: 49
CRNCYSTR :: 50
} else {
#panic("posix is unimplemented for the current target")
}

View File

@@ -0,0 +1,74 @@
package posix
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// libgen.h - definitions for pattern matching functions
foreign lib {
/*
Takes the pathname pointed to by path and return a pointer to the final component of the
pathname, deleting any trailing '/' characters.
NOTE: may modify input, so don't give it string literals.
Returns: a string that might be a modification of the input string or a static string overwritten by subsequent calls
Example:
tests := []string{
"usr", "usr/", "", "/", "//", "///", "/usr/", "/usr/lib",
"//usr//lib//", "/home//dwc//test",
}
tbl: table.Table
table.init(&tbl)
table.header(&tbl, "input", "dirname", "basename")
for test in tests {
din := strings.clone_to_cstring(test); defer delete(din)
dir := strings.clone_from_cstring(posix.dirname(din))
bin := strings.clone_to_cstring(test); defer delete(bin)
base := strings.clone_from_cstring(posix.basename(bin))
table.row(&tbl, test, dir, base)
}
table.write_plain_table(os.stream_from_handle(os.stdout), &tbl)
Output:
+----------------+----------+--------+
|input |dirname |basename|
+----------------+----------+--------+
|usr |. |usr |
|usr/ |. |usr |
| |. |. |
|/ |/ |/ |
|// |/ |/ |
|/// |/ |/ |
|/usr/ |/ |usr |
|/usr/lib |/usr |lib |
|//usr//lib// |//usr |lib |
|/home//dwc//test|/home//dwc|test |
+----------------+----------+--------+
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/basename.html ]]
*/
basename :: proc(path: cstring) -> cstring ---
/*
Takes a string that contains a pathname, and returns a string that is a pathname of the parent
directory of that file.
NOTE: may modify input, so don't give it string literals.
Returns: a string that might be a modification of the input string or a static string overwritten by subsequent calls
See example for basename().
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/dirname.html ]]
*/
dirname :: proc(path: cstring) -> cstring ---
}

459
core/sys/posix/limits.odin Normal file
View File

@@ -0,0 +1,459 @@
package posix
// limits.h - implementation-defined constants
// NOTE: numerical limits are left out because Odin provides `min(T)` and `max(T)`.
// The <limits.h> header shall define the following symbolic constants with the values shown.
// These are the most restrictive values for certain features on an implementation.
// A conforming implementation shall provide values no larger than these values.
// A conforming application must not require a smaller value for correct operation.
_POSIX_CLOCKRES_MIN :: 20000000
// The <limits.h> header shall define the following symbolic constants with the values shown.
// These are the most restrictive values for certain features on an implementation conforming to
// this volume of POSIX.1-2017.
// Related symbolic constants are defined elsewhere in this volume of POSIX.1-2017 which reflect
// the actual implementation and which need not be as restrictive. For each of these limits,
// a conforming implementation shall provide a value at least this large or shall have no limit.
// A strictly conforming application must not require a larger value for correct operation.
_POSIX_AIO_LISTIO_MAX :: 2
_POSIX_AIO_MAX :: 1
_POSIX_ARG_MAX :: 4096
_POSIX_CHILD_MAX :: 25
_POSIX_DELAYTIMER_MAX :: 32
_POSIX_HOST_NAME_MAX :: 255
_POSIX_LINK_MAX :: 8
_POSIX_MAX_CANON :: 255
_POSIX_MAX_INPUT :: 255
_POSIX_MQ_OPEN_MAX :: 8
_POSIX_MQ_PRIO_MAX :: 32
_POSIX_NAME_MAX :: 14
_POSIX_NGROUPS_MAX :: 8
_POSIX_OPEN_MAX :: 20
_POSIX_PATH_MAX :: 256
_POSIX_PIPE_BUF :: 512
_POSIX_RE_DUP_MAX :: 255
_POSIX_RTSIG_MAX :: 8
_POSIX_SEM_NSEMS_MAX :: 256
_POSIX_SEM_VALUE_MAX :: 32767
_POSIX_SS_REPL_MAX :: 4
_POSIX_STREAM_MAX :: 8
_POSIX_SYMLINK_MAX :: 255
_POSIX_SYMLOOP_MAX :: 8
_POSIX_THREAD_DESTRUCTION_ITERATIONS :: 4
_POSIX_THREAD_KEYS_MAX :: 128
_POSIX_THREADS_THREADS_MAX :: 64
_POSIX_TIMER_MAX :: 32
_POSIX_TRAXE_EVENT_NAME_MAX :: 30
_POSIX_TRACE_NAME_MAX :: 8
_POSIX_TRACE_SYS_MAX :: 8
_POSIX_TRACE_USER_EVENT_MAX :: 32
_POSIX_TTY_NAME_MAX :: 9
_POSIX_TZNAME_MAX :: 6
_POSIX2_BC_BASE_MAX :: 99
_POSIX2_BC_DIM_MAX :: 2048
_POSIX2_BC_SCALE_MAX :: 99
_POSIX2_CHARCLASS_NAME_MAX :: 14
_POSIX2_COLL_WEIGHTS_MAX :: 2
_POSIX2_EXPR_NEST_MAX :: 32
_POSIX2_LINE_MAX :: 2048
_POSIX2_RE_DUP_MAX :: 255
_XOPEN_IOV_MAX :: 16
_XOPEN_NAME_MAX :: 255
_XOPEN_PATH_MAX :: 1024
/*
NOTE: for full portability, usage should look something like:
page_size: uint
when #defined(posix.PAGESIZE) {
page_size = posix.PAGESIZE
} else {
page_size = posix.sysconf(._PAGESIZE)
}
*/
when ODIN_OS == .Darwin {
// A definition of one of the symbolic constants in the following list shall be omitted from
// <limits.h> on specific implementations where the corresponding value is equal to or greater
// than the stated minimum, but is unspecified.
//
// This indetermination might depend on the amount of available memory space on a specific
// instance of a specific implementation. The actual value supported by a specific instance shall
// be provided by the sysconf() function.
// AIO_LISTIO_MAX :: sysconf(._AIO_LISTIO_MAX)
// AIO_MAX :: sysconf(._AIO_MAX)
// AIO_PRIO_DELTA_MAX :: sysconf(._AIO_PRIO_DELTA_MAX)
ARG_MAX :: 1024 * 1024
// ATEXIT_MAX :: sysconf(._ATEXIT_MAX)
CHILD_MAX :: 266
// DELAYTIMER_MAX :: sysconf(._DELAYTIMER_MAX)
// HOST_NAME_MAX :: sysconf(._HOST_NAME_MAX)
IOV_MAX :: 1024
// LOGIN_NAME_MAX :: sysconf(._LOGIN_NAME_MAX)
// MQ_OPEN_MAX :: sysconf(._MQ_OPEN_MAX)
// MQ_PRIO_MAX :: sysconf(._MQ_PRIO_MAX)
PAGESIZE :: PAGE_SIZE
PAGE_SIZE :: 1 << 12
PTHREAD_DESTRUCTOR_ITERATIONS :: 4
PTHREAD_KEYS_MAX :: 512
PTHREAD_STACK_MIN :: 16384 when ODIN_ARCH == .arm64 else 8192
// RTSIG_MAX :: sysconf(._RTSIG_MAX)
// SEM_NSEMS_MAX :: sysconf(._SEM_NSEMS_MAX)
// SEM_VALUE_MAX :: sysconf(._SEM_VALUE_MAX)
// SIGQUEUE_MAX :: sysconf(._SIGQUEUE_MAX)
// SS_REPL_MAX :: sysconf(._SS_REPL_MAX)
// STREAM_MAX :: sysconf(._STREAM_MAX)
// SYMLOOP_MAX :: sysconf(._SYMLOOP_MAX)
// TIMER_MAX :: sysconf(._TIMER_MAX)
// TRACE_EVENT_NAME_MAX :: sysconf(._TRACE_EVENT_NAME_MAX)
// TRACE_NAME_MAX :: sysconf(._TRACE_NAME_MAX)
// TRACE_SYS_MAX :: sysconf(._TRACE_SYS_MAX)
// TRACE_USER_EVENT_MAX :: sysconf(._TRACE_USER_EVENT_MAX)
// TTY_NAME_MAX :: sysconf(._TTY_NAME_MAX)
// TZNAME_MAX :: sysconf(._TZNAME_MAX)
// The values in the following list may be constants within an implementation or may vary from
// one pathname to another.
// For example, file systems or directories may have different characteristics.
//
// A definition of one of the symbolic constants in the following list shall be omitted from the
// <limits.h> header on specific implementations where the corresponding value is equal to or
// greater than the stated minimum, but where the value can vary depending on the file to which
// it is applied.
// The actual value supported for a specific pathname shall be provided by the pathconf() function.
// FILESIZEBITS :: pathconf(".", ._FILESIZEBITS)
LINK_MAX :: 32767
MAX_CANON :: 1024
MAX_INPUT :: 1024
NAME_MAX :: 255
PATH_MAX :: 1024
PIPE_BUF :: 512
// POSIX_ALLOC_SIZE_MIN :: pathconf("foo.txt", ._POSIX_ALLOC_SIZE_MIN)
// POSIX_REC_INCR_XFER_SIZE :: pathconf("foo.txt", ._POSIX_REC_INCR_XFER_SIZE)
// POSIX_REC_MAX_XFER_SIZE :: pathconf("foo.txt", ._POSIX_REC_MAX_XFER_SIZE)
// POSIX_REC_MIN_XFER_SIZE :: pathconf("foo.txt", ._POSIX_REC_MIN_XFER_SIZE)
// POSIX_REC_XFER_ALIGN :: pathconf("foo.txt", ._POSIX_REC_XFER_ALIGN)
// SYMLINK_MAX :: pathconf(".", ._SYMLINK_MAX)
// The magnitude limitations in the following list shall be fixed by specific implementations.
// An application should assume that the value of the symbolic constant defined by <limits.h>
// in a specific implementation is the minimum that pertains whenever the application is run
// under that implementation.
// A specific instance of a specific implementation may increase the value relative to that
// supplied by <limits.h> for that implementation.
// The actual value supported by a specific instance shall be provided by the sysconf() function.
BC_BASE_MAX :: 99
BC_DIM_MAX :: 2048
BC_SCALE_MAX :: 99
BC_STRING_MAX :: 1000
CHARCLASS_NAME_MAX :: 14
COLL_WEIGHTS_MAX :: 2
EXPR_NEST_MAX :: 2
LINE_MAX :: 2048
NGROUPS_MAX :: 16
RE_DUP_MAX :: 255
// Other limits.
NL_ARGMAX :: 9
NL_LANGMAX :: 14
NL_MSGMAX :: 32767
NL_SETMAX :: 255
NL_TEXTMAX :: 2048
NZERO :: 20
} else when ODIN_OS == .FreeBSD {
// A definition of one of the symbolic constants in the following list shall be omitted from
// <limits.h> on specific implementations where the corresponding value is equal to or greater
// than the stated minimum, but is unspecified.
//
// This indetermination might depend on the amount of available memory space on a specific
// instance of a specific implementation. The actual value supported by a specific instance shall
// be provided by the sysconf() function.
// AIO_LISTIO_MAX :: sysconf(._AIO_LISTIO_MAX)
// AIO_MAX :: sysconf(._AIO_MAX)
// AIO_PRIO_DELTA_MAX :: sysconf(._AIO_PRIO_DELTA_MAX)
ARG_MAX :: 2 * 256 * 1024
// ATEXIT_MAX :: sysconf(._ATEXIT_MAX)
CHILD_MAX :: 40
// DELAYTIMER_MAX :: sysconf(._DELAYTIMER_MAX)
// HOST_NAME_MAX :: sysconf(._HOST_NAME_MAX)
IOV_MAX :: 1024
// LOGIN_NAME_MAX :: sysconf(._LOGIN_NAME_MAX)
// MQ_OPEN_MAX :: sysconf(._MQ_OPEN_MAX)
MQ_PRIO_MAX :: 64
PAGESIZE :: PAGE_SIZE
PAGE_SIZE :: 1 << 12
PTHREAD_DESTRUCTOR_ITERATIONS :: 4
PTHREAD_KEYS_MAX :: 256
PTHREAD_STACK_MIN :: MINSIGSTKSZ
// RTSIG_MAX :: sysconf(._RTSIG_MAX)
// SEM_NSEMS_MAX :: sysconf(._SEM_NSEMS_MAX)
// SEM_VALUE_MAX :: sysconf(._SEM_VALUE_MAX)
// SIGQUEUE_MAX :: sysconf(._SIGQUEUE_MAX)
// SS_REPL_MAX :: sysconf(._SS_REPL_MAX)
// STREAM_MAX :: sysconf(._STREAM_MAX)
// SYMLOOP_MAX :: sysconf(._SYMLOOP_MAX)
// TIMER_MAX :: sysconf(._TIMER_MAX)
// TRACE_EVENT_NAME_MAX :: sysconf(._TRACE_EVENT_NAME_MAX)
// TRACE_NAME_MAX :: sysconf(._TRACE_NAME_MAX)
// TRACE_SYS_MAX :: sysconf(._TRACE_SYS_MAX)
// TRACE_USER_EVENT_MAX :: sysconf(._TRACE_USER_EVENT_MAX)
// TTY_NAME_MAX :: sysconf(._TTY_NAME_MAX)
// TZNAME_MAX :: sysconf(._TZNAME_MAX)
// The values in the following list may be constants within an implementation or may vary from
// one pathname to another.
// For example, file systems or directories may have different characteristics.
//
// A definition of one of the symbolic constants in the following list shall be omitted from the
// <limits.h> header on specific implementations where the corresponding value is equal to or
// greater than the stated minimum, but where the value can vary depending on the file to which
// it is applied.
// The actual value supported for a specific pathname shall be provided by the pathconf() function.
// FILESIZEBITS :: pathconf(".", ._FILESIZEBITS)
// LINK_MAX :: pathconf(foo.txt", ._LINK_MAX)
MAX_CANON :: 255
MAX_INPUT :: 255
NAME_MAX :: 255
PATH_MAX :: 1024
PIPE_BUF :: 512
// POSIX_ALLOC_SIZE_MIN :: pathconf("foo.txt", ._POSIX_ALLOC_SIZE_MIN)
// POSIX_REC_INCR_XFER_SIZE :: pathconf("foo.txt", ._POSIX_REC_INCR_XFER_SIZE)
// POSIX_REC_MAX_XFER_SIZE :: pathconf("foo.txt", ._POSIX_REC_MAX_XFER_SIZE)
// POSIX_REC_MIN_XFER_SIZE :: pathconf("foo.txt", ._POSIX_REC_MIN_XFER_SIZE)
// POSIX_REC_XFER_ALIGN :: pathconf("foo.txt", ._POSIX_REC_XFER_ALIGN)
// SYMLINK_MAX :: pathconf(".", ._SYMLINK_MAX)
// The magnitude limitations in the following list shall be fixed by specific implementations.
// An application should assume that the value of the symbolic constant defined by <limits.h>
// in a specific implementation is the minimum that pertains whenever the application is run
// under that implementation.
// A specific instance of a specific implementation may increase the value relative to that
// supplied by <limits.h> for that implementation.
// The actual value supported by a specific instance shall be provided by the sysconf() function.
BC_BASE_MAX :: 99
BC_DIM_MAX :: 2048
BC_SCALE_MAX :: 99
BC_STRING_MAX :: 1000
CHARCLASS_NAME_MAX :: 14
COLL_WEIGHTS_MAX :: 10
EXPR_NEST_MAX :: 32
LINE_MAX :: 2048
NGROUPS_MAX :: 1023
RE_DUP_MAX :: 255
// Other limits.
NL_ARGMAX :: 4096
NL_LANGMAX :: 31
NL_MSGMAX :: 32767
NL_SETMAX :: 255
NL_TEXTMAX :: 2048
NZERO :: 0
} else when ODIN_OS == .NetBSD {
// A definition of one of the symbolic constants in the following list shall be omitted from
// <limits.h> on specific implementations where the corresponding value is equal to or greater
// than the stated minimum, but is unspecified.
//
// This indetermination might depend on the amount of available memory space on a specific
// instance of a specific implementation. The actual value supported by a specific instance shall
// be provided by the sysconf() function.
// AIO_LISTIO_MAX :: sysconf(._AIO_LISTIO_MAX)
// AIO_MAX :: sysconf(._AIO_MAX)
// AIO_PRIO_DELTA_MAX :: sysconf(._AIO_PRIO_DELTA_MAX)
ARG_MAX :: 256 * 1024
// ATEXIT_MAX :: sysconf(._ATEXIT_MAX)
CHILD_MAX :: 160
// DELAYTIMER_MAX :: sysconf(._DELAYTIMER_MAX)
// HOST_NAME_MAX :: sysconf(._HOST_NAME_MAX)
IOV_MAX :: 1024
LOGIN_NAME_MAX :: 17
MQ_OPEN_MAX :: 512
MQ_PRIO_MAX :: 32
PAGESIZE :: PAGE_SIZE
PAGE_SIZE :: 1 << 12
PTHREAD_DESTRUCTOR_ITERATIONS :: 4
PTHREAD_KEYS_MAX :: 256
// PTHREAD_STACK_MIN :: sysconf(._THREAD_STACK_MIN)
// RTSIG_MAX :: sysconf(._RTSIG_MAX)
// SEM_NSEMS_MAX :: sysconf(._SEM_NSEMS_MAX)
// SEM_VALUE_MAX :: sysconf(._SEM_VALUE_MAX)
// SIGQUEUE_MAX :: sysconf(._SIGQUEUE_MAX)
// SS_REPL_MAX :: sysconf(._SS_REPL_MAX)
// STREAM_MAX :: sysconf(._STREAM_MAX)
// SYMLOOP_MAX :: sysconf(._SYMLOOP_MAX)
// TIMER_MAX :: sysconf(._TIMER_MAX)
// TRACE_EVENT_NAME_MAX :: sysconf(._TRACE_EVENT_NAME_MAX)
// TRACE_NAME_MAX :: sysconf(._TRACE_NAME_MAX)
// TRACE_SYS_MAX :: sysconf(._TRACE_SYS_MAX)
// TRACE_USER_EVENT_MAX :: sysconf(._TRACE_USER_EVENT_MAX)
// TTY_NAME_MAX :: sysconf(._TTY_NAME_MAX)
// TZNAME_MAX :: sysconf(._TZNAME_MAX)
// The values in the following list may be constants within an implementation or may vary from
// one pathname to another.
// For example, file systems or directories may have different characteristics.
//
// A definition of one of the symbolic constants in the following list shall be omitted from the
// <limits.h> header on specific implementations where the corresponding value is equal to or
// greater than the stated minimum, but where the value can vary depending on the file to which
// it is applied.
// The actual value supported for a specific pathname shall be provided by the pathconf() function.
// FILESIZEBITS :: pathconf(".", ._FILESIZEBITS)
LINK_MAX :: 32767
MAX_CANON :: 255
MAX_INPUT :: 255
NAME_MAX :: 511
PATH_MAX :: 1024
PIPE_BUF :: 512
// POSIX_ALLOC_SIZE_MIN :: pathconf("foo.txt", ._POSIX_ALLOC_SIZE_MIN)
// POSIX_REC_INCR_XFER_SIZE :: pathconf("foo.txt", ._POSIX_REC_INCR_XFER_SIZE)
// POSIX_REC_MAX_XFER_SIZE :: pathconf("foo.txt", ._POSIX_REC_MAX_XFER_SIZE)
// POSIX_REC_MIN_XFER_SIZE :: pathconf("foo.txt", ._POSIX_REC_MIN_XFER_SIZE)
// POSIX_REC_XFER_ALIGN :: pathconf("foo.txt", ._POSIX_REC_XFER_ALIGN)
// SYMLINK_MAX :: pathconf(".", ._SYMLINK_MAX)
// The magnitude limitations in the following list shall be fixed by specific implementations.
// An application should assume that the value of the symbolic constant defined by <limits.h>
// in a specific implementation is the minimum that pertains whenever the application is run
// under that implementation.
// A specific instance of a specific implementation may increase the value relative to that
// supplied by <limits.h> for that implementation.
// The actual value supported by a specific instance shall be provided by the sysconf() function.
BC_BASE_MAX :: max(i32)
BC_DIM_MAX :: 65535
BC_SCALE_MAX :: max(i32)
BC_STRING_MAX :: max(i32)
CHARCLASS_NAME_MAX :: 14
COLL_WEIGHTS_MAX :: 2
EXPR_NEST_MAX :: 32
LINE_MAX :: 2048
NGROUPS_MAX :: 16
RE_DUP_MAX :: 255
// Other limits.
NL_ARGMAX :: 9
NL_LANGMAX :: 14
NL_MSGMAX :: 32767
NL_SETMAX :: 255
NL_TEXTMAX :: 2048
NZERO :: 20
} else when ODIN_OS == .OpenBSD {
// A definition of one of the symbolic constants in the following list shall be omitted from
// <limits.h> on specific implementations where the corresponding value is equal to or greater
// than the stated minimum, but is unspecified.
//
// This indetermination might depend on the amount of available memory space on a specific
// instance of a specific implementation. The actual value supported by a specific instance shall
// be provided by the sysconf() function.
// AIO_LISTIO_MAX :: sysconf(._AIO_LISTIO_MAX)
// AIO_MAX :: sysconf(._AIO_MAX)
// AIO_PRIO_DELTA_MAX :: sysconf(._AIO_PRIO_DELTA_MAX)
ARG_MAX :: 512 * 1024
// ATEXIT_MAX :: sysconf(._ATEXIT_MAX)
CHILD_MAX :: 80
// DELAYTIMER_MAX :: sysconf(._DELAYTIMER_MAX)
// HOST_NAME_MAX :: sysconf(._HOST_NAME_MAX)
IOV_MAX :: 1024
LOGIN_NAME_MAX :: 32
MQ_OPEN_MAX :: 512
MQ_PRIO_MAX :: 32
PAGESIZE :: PAGE_SIZE
PAGE_SIZE :: 1 << 12
PTHREAD_DESTRUCTOR_ITERATIONS :: 4
PTHREAD_KEYS_MAX :: 256
PTHREAD_STACK_MIN :: 1 << 12
// RTSIG_MAX :: sysconf(._RTSIG_MAX)
// SEM_NSEMS_MAX :: sysconf(._SEM_NSEMS_MAX)
SEM_VALUE_MAX :: max(u32)
// SIGQUEUE_MAX :: sysconf(._SIGQUEUE_MAX)
// SS_REPL_MAX :: sysconf(._SS_REPL_MAX)
// STREAM_MAX :: sysconf(._STREAM_MAX)
SYMLOOP_MAX :: 32
// TIMER_MAX :: sysconf(._TIMER_MAX)
// TRACE_EVENT_NAME_MAX :: sysconf(._TRACE_EVENT_NAME_MAX)
// TRACE_NAME_MAX :: sysconf(._TRACE_NAME_MAX)
// TRACE_SYS_MAX :: sysconf(._TRACE_SYS_MAX)
// TRACE_USER_EVENT_MAX :: sysconf(._TRACE_USER_EVENT_MAX)
// TTY_NAME_MAX :: sysconf(._TTY_NAME_MAX)
// TZNAME_MAX :: sysconf(._TZNAME_MAX)
// The values in the following list may be constants within an implementation or may vary from
// one pathname to another.
// For example, file systems or directories may have different characteristics.
//
// A definition of one of the symbolic constants in the following list shall be omitted from the
// <limits.h> header on specific implementations where the corresponding value is equal to or
// greater than the stated minimum, but where the value can vary depending on the file to which
// it is applied.
// The actual value supported for a specific pathname shall be provided by the pathconf() function.
// FILESIZEBITS :: pathconf(".", ._FILESIZEBITS)
LINK_MAX :: 32767
MAX_CANON :: 255
MAX_INPUT :: 255
NAME_MAX :: 255
PATH_MAX :: 1024
PIPE_BUF :: 512
// POSIX_ALLOC_SIZE_MIN :: pathconf("foo.txt", ._POSIX_ALLOC_SIZE_MIN)
// POSIX_REC_INCR_XFER_SIZE :: pathconf("foo.txt", ._POSIX_REC_INCR_XFER_SIZE)
// POSIX_REC_MAX_XFER_SIZE :: pathconf("foo.txt", ._POSIX_REC_MAX_XFER_SIZE)
// POSIX_REC_MIN_XFER_SIZE :: pathconf("foo.txt", ._POSIX_REC_MIN_XFER_SIZE)
// POSIX_REC_XFER_ALIGN :: pathconf("foo.txt", ._POSIX_REC_XFER_ALIGN)
SYMLINK_MAX :: PATH_MAX
// The magnitude limitations in the following list shall be fixed by specific implementations.
// An application should assume that the value of the symbolic constant defined by <limits.h>
// in a specific implementation is the minimum that pertains whenever the application is run
// under that implementation.
// A specific instance of a specific implementation may increase the value relative to that
// supplied by <limits.h> for that implementation.
// The actual value supported by a specific instance shall be provided by the sysconf() function.
BC_BASE_MAX :: max(i32)
BC_DIM_MAX :: 65535
BC_SCALE_MAX :: max(i32)
BC_STRING_MAX :: max(i32)
CHARCLASS_NAME_MAX :: 14
COLL_WEIGHTS_MAX :: 2
EXPR_NEST_MAX :: 32
LINE_MAX :: 2048
NGROUPS_MAX :: 16
RE_DUP_MAX :: 255
// Other limits.
NL_ARGMAX :: 9
NL_LANGMAX :: 14
NL_MSGMAX :: 32767
NL_SETMAX :: 255
NL_TEXTMAX :: 255
NZERO :: 20
} else {
#panic("posix is unimplemented for the current target")
}

View File

@@ -0,0 +1,93 @@
package posix
import "core:c"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// locale.h - category macros
foreign lib {
/*
Sets the components of an object with the type lconv with the values appropriate for the
formatting of numeric quantities (monetary and otherwise) according to the rules of the current
locale.
Returns: a pointer to the lconv structure, might be invalidated by subsequent calls to localeconv() and setlocale()
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/localeconv.html ]]
*/
localeconv :: proc() -> ^lconv ---
/*
Selects the appropriate piece of the global locale, as specified by the category and locale arguments,
and can be used to change or query the entire global locale or portions thereof.
Returns: the current locale if `locale` is `nil`, the set locale otherwise
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setlocale.html ]]
*/
@(link_name=LSETLOCALE)
setlocale :: proc(category: Locale_Category, locale: cstring) -> cstring ---
}
Locale_Category :: enum c.int {
ALL = LC_ALL,
COLLATE = LC_COLLATE,
CTYPE = LC_CTYPE,
MESSAGES = LC_MESSAGES,
MONETARY = LC_MONETARY,
NUMERIC = LC_NUMERIC,
TIME = LC_TIME,
}
when ODIN_OS == .NetBSD {
@(private) LSETLOCALE :: "__setlocale50"
} else {
@(private) LSETLOCALE :: "setlocale"
}
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
// NOTE: All of these fields are standard ([PSX]).
lconv :: struct {
decimal_point: cstring,
thousand_sep: cstring,
grouping: cstring,
int_curr_symbol: cstring,
currency_symbol: cstring,
mon_decimal_points: cstring,
mon_thousands_sep: cstring,
mon_grouping: cstring,
positive_sign: cstring,
negative_sign: cstring,
int_frac_digits: c.char,
frac_digits: c.char,
p_cs_precedes: c.char,
p_sep_by_space: c.char,
n_cs_precedes: c.char,
n_sep_by_space: c.char,
p_sign_posn: c.char,
n_sign_posn: c.char,
int_p_cs_precedes: c.char,
int_n_cs_precedes: c.char,
int_p_sep_by_space: c.char,
int_n_sep_by_space: c.char,
int_p_sign_posn: c.char,
int_n_sign_posn: c.char,
}
LC_ALL :: 0
LC_COLLATE :: 1
LC_CTYPE :: 2
LC_MESSAGES :: 6
LC_MONETARY :: 3
LC_NUMERIC :: 4
LC_TIME :: 5
} else {
#panic("posix is unimplemented for the current target")
}

View File

@@ -0,0 +1,42 @@
package posix
import "core:c"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// monetary.h - monetary types
foreign lib {
/*
Places characters into the array pointed to by s as controlled by the string format.
No more than maxsize bytes are placed into the array.
Returns: -1 (setting errno) on failure, the number of bytes added to s otherwise
Example:
posix.setlocale(.ALL, "en_US.UTF-8")
value := 123456.789
buffer: [100]byte
size := posix.strfmon(raw_data(buffer[:]), len(buffer), "%n", value)
if int(size) == -1 {
fmt.panicf("strfmon failure: %s", posix.strerror(posix.errno()))
}
fmt.println(string(buffer[:size]))
Output:
$123,456.79
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/strfmon.html ]]
*/
strfmon :: proc(
s: [^]byte,
maxsize: c.size_t,
format: cstring,
#c_vararg args: ..any,
) -> c.size_t ---
}

View File

@@ -0,0 +1,60 @@
package posix
import "core:c"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// net/if.h - sockets local interfaces
foreign lib {
/*
Retrieve an array of name indexes. Where the last one has an index of 0 and name of nil.
Returns: nil (setting errno) on failure, an array that should be freed with if_freenameindex otherwise
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/if_nameindex.html ]]
*/
if_nameindex :: proc() -> [^]if_nameindex_t ---
/*
Returns the interface index matching the name or zero.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/if_nametoindex.html ]]
*/
if_nametoindex :: proc(name: cstring) -> c.uint ---
/*
Returns the name corresponding to the index.
ifname should be at least IF_NAMESIZE bytes in size.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/if_indextoname.html ]]
*/
if_indextoname :: proc(ifindex: c.uint, ifname: [^]byte) -> cstring ---
/*
Frees memory allocated by if_nameindex.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/if_freenameindex.html ]]
*/
if_freenameindex :: proc(ptr: ^if_nameindex_t) ---
}
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
// NOTE: `_t` suffix added due to name conflict.
if_nameindex_t :: struct {
if_index: c.uint, /* [PSX] 1, 2, ... */
if_name: cstring, /* [PSX] null terminated name: "le0", ... */
}
IF_NAMESIZE :: 16
} else {
#panic("posix is unimplemented for the current target")
}

443
core/sys/posix/netdb.odin Normal file
View File

@@ -0,0 +1,443 @@
package posix
import "core:c"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// netdb.h - definitions for network database operations
foreign lib {
/*
Translate node/serv name and return a set of socket addresses and associated information to be
used in creating a socket with which to address the specified service.
Example:
// The following (incomplete) program demonstrates the use of getaddrinfo() to obtain the
// socket address structure(s) for the service named in the program's command-line argument.
// The program then loops through each of the address structures attempting to create and bind
// a socket to the address, until it performs a successful bind().
args := runtime.args__
if len(args) != 2 {
fmt.eprintfln("Usage: %s port", args[0])
posix.exit(1)
}
hints: posix.addrinfo
hints.ai_socktype = .DGRAM
hints.ai_flags = { .PASSIVE }
result: ^posix.addrinfo
s := posix.getaddrinfo(nil, args[1], &hints, &result)
if s != .NONE {
fmt.eprintfln("getaddrinfo: %s", posix.gai_strerror(s))
posix.exit(1)
}
defer posix.freeaddrinfo(result)
// Try each address until a successful bind().
rp: ^posix.addrinfo
for rp = result; rp != nil; rp = rp.ai_next {
sfd := posix.socket(rp.ai_family, rp.ai_socktype, rp.ai_protocol)
if sfd == -1 {
continue
}
if posix.bind(sfd, rp.ai_addr, rp.ai_addrlen) == 0 {
// Success.
break
}
posix.close(sfd)
}
if rp == nil {
fmt.eprintln("Could not bind")
posix.exit(1)
}
// Use the socket...
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getaddrinfo.html ]]
*/
getaddrinfo :: proc(
nodename: cstring,
servname: cstring,
hints: ^addrinfo,
res: ^^addrinfo,
) -> Info_Errno ---
/*
Frees the given address info linked list.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getaddrinfo.html ]]
*/
freeaddrinfo :: proc(ai: ^addrinfo) ---
/*
Translate a socket address to a node name and service location.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getnameinfo.html ]]
*/
getnameinfo :: proc(
sa: ^sockaddr, salen: socklen_t,
node: [^]byte, nodelen: socklen_t,
service: [^]byte, servicelen: socklen_t,
flags: Nameinfo_Flags,
) -> Info_Errno ---
/*
Get a textual description for the address info errors.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/gai_strerror.html ]]
*/
gai_strerror :: proc(ecode: Info_Errno) -> cstring ---
/*
Opens a connection to the database and set the next entry to the first entry in the database.
This reads /etc/hosts on most systems.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sethostent.html ]]
*/
sethostent :: proc(stayopen: b32) ---
/*
Reads the next entry in the database, opening and closing a connection as necessary.
This reads /etc/hosts on most systems.
Example:
posix.sethostent(true)
defer posix.endhostent()
for ent := posix.gethostent(); ent != nil; ent = posix.gethostent() {
fmt.println(ent)
fmt.println(ent.h_addr_list[0][:ent.h_length])
}
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sethostent.html ]]
*/
gethostent :: proc() -> ^hostent ---
/*
Closes the connection to the database.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sethostent.html ]]
*/
endhostent :: proc() ---
/*
Opens and rewinds the database.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setnetent.html ]]
*/
setnetent :: proc(stayopen: b32) ---
/*
Reads the next entry of the database.
Example:
posix.setnetent(true)
defer posix.endnetent()
for ent := posix.getnetent(); ent != nil; ent = posix.getnetent() {
fmt.println(ent)
fmt.println(transmute([4]byte)ent.n_net)
}
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setnetent.html ]]
*/
getnetent :: proc() -> ^netent ---
/*
Search the database from the beginning, and find the first entry that matches.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setnetent.html ]]
*/
getnetbyaddr :: proc(net: c.uint32_t, type: AF) -> ^netent ---
/*
Search the database from the beginning, and find the first entry that matches.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setnetent.html ]]
*/
getnetbyname :: proc(name: cstring) -> ^netent ---
/*
Closes the database.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setnetent.html ]]
*/
endnetent :: proc() ---
/*
Opens and rewinds the database.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setprotoent.html ]]
*/
setprotoent :: proc(stayopen: b32) ---
/*
Reads the next entry of the database.
Example:
posix.setprotoent(true)
defer posix.endprotoent()
for ent := posix.getprotoent(); ent != nil; ent = posix.getprotoent() {
fmt.println(ent)
}
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setprotoent.html ]]
*/
getprotoent :: proc() -> ^protoent ---
/*
Search the database from the beginning, and find the first entry that matches.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setprotoent.html ]]
*/
getprotobyname :: proc(name: cstring) -> ^protoent ---
/*
Search the database from the beginning, and find the first entry that matches.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setprotoent.html ]]
*/
getprotobynumber :: proc(proto: c.int) -> ^protoent ---
/*
Closes the database.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setprotoent.html ]]
*/
endprotoent :: proc() ---
/*
Opens and rewinds the database.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setservent.html ]]
*/
setservent :: proc(stayopen: b32) ---
/*
Reads the next entry of the database.
Example:
posix.setservent(true)
defer posix.endservent()
for ent := posix.getservent(); ent != nil; ent = posix.getservent() {
fmt.println(ent)
}
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setservent.html ]]
*/
getservent :: proc() -> ^servent ---
/*
Search the database from the beginning, and find the first entry that matches.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setservent.html ]]
*/
getservbyname :: proc(name: cstring, proto: cstring) -> ^servent ---
/*
Search the database from the beginning, and find the first entry that matches.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setservent.html ]]
*/
getservbyport :: proc(port: c.int, proto: cstring) -> ^servent ---
/*
Closes the database.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setservent.html ]]
*/
endservent :: proc() ---
}
Addrinfo_Flag_Bits :: enum c.int {
// Socket address is intended for bind().
PASSIVE = log2(AI_PASSIVE),
// Request for canonical name.
CANONNAME = log2(AI_CANONNAME),
// Return numeric host address as name.
NUMERICHOST = log2(AI_NUMERICHOST),
// Inhibit service name resolution.
NUMERICSERV = log2(AI_NUMERICSERV),
// If no IPv6 addresses are found, query for IPv4 addresses and return them to the
// caller as IPv4-mapped IPv6 addresses.
V4MAPPED = log2(AI_V4MAPPED),
// Query for both IPv4 and IPv6 addresses.
ALL = log2(AI_ALL),
// Query for IPv4 addresses only when an IPv4 address is configured; query for IPv6 addresses
// only when an IPv6 address is configured.
ADDRCONFIG = log2(AI_ADDRCONFIG),
}
Addrinfo_Flags :: bit_set[Addrinfo_Flag_Bits; c.int]
Nameinfo_Flag_Bits :: enum c.int {
// Only the nodename portion of the FQDN is returned for local hosts.
NOFQDN = log2(NI_NOFQDN),
// The numeric form of the node's address is returned instead of its name.
NUMERICHOST = log2(NI_NUMERICHOST),
// Return an error if the node's name cannot be located in the database.
NAMEREQD = log2(NI_NAMEREQD),
// The numeric form of the service address is returned instead of its name.
NUMERICSERV = log2(NI_NUMERICSERV),
// For IPv6 addresses, the numeric form of the scope identifier is returned instead of its name.
NUMERICSCOPE = log2(NI_NUMERICSCOPE),
// Indicates that the service is a datagram service (SOCK_DGRAM).
DGRAM = log2(NI_DGRAM),
}
Nameinfo_Flags :: bit_set[Nameinfo_Flag_Bits; c.int]
Info_Errno :: enum c.int {
NONE = 0,
// The name could not be resolved at this time. Future attempts may succeed.
AGAIN = EAI_AGAIN,
// The flags had an invalid value.
BADFLAGS = EAI_BADFLAGS,
// A non-recoverable error ocurred.
FAIL = EAI_FAIL,
// The address family was not recognized or the address length was invald for the specified family.
FAMILY = EAI_FAMILY,
// There was a memory allocation failure.
MEMORY = EAI_MEMORY,
// The name does not resolve for the supplied parameters.
NONAME = EAI_NONAME,
// The service passed was not recognized for the specified socket.
SERVICE = EAI_SERVICE,
// The intended socket type was not recognized.
SOCKTYPE = EAI_SOCKTYPE,
// A system error occurred. The error code can be found in errno.
SYSTEM = EAI_SYSTEM,
// An argument buffer overflowed.
OVERFLOW = EAI_OVERFLOW,
}
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
hostent :: struct {
h_name: cstring, /* [PSX] official name of host */
h_aliases: [^]cstring `fmt:"v,0"`, /* [PSX] alias list */
h_addrtype: AF, /* [PSX] host address type */
h_length: c.int, /* [PSX] length of address */
h_addr_list: [^][^]byte `fmt:"v,0"`, /* [PSX] list of addresses from name server */
}
netent :: struct {
n_name: cstring, /* [PSX] official name of net */
n_aliases: [^]cstring `fmt:"v,0"`, /* [PSX] alias list */
n_addrtype: AF, /* [PSX] net address type */
n_net: c.uint32_t, /* [PSX] network # */
}
protoent :: struct {
p_name: cstring, /* [PSX] official protocol name */
p_aliases: [^]cstring `fmt:"v,0"`, /* [PSX] alias list */
p_proto: c.int, /* [PSX] protocol # */
}
servent :: struct {
s_name: cstring, /* [PSX] official service name */
s_aliases: [^]cstring `fmt:"v,0"`, /* [PSX] alias list */
s_port: c.int, /* [PSX] port # */
s_proto: cstring, /* [PSX] protocol # */
}
// The highest reserved port number.
IPPORT_RESERVED :: 1024
addrinfo :: struct {
ai_flags: Addrinfo_Flags, /* [PSX] input flags */
ai_family: AF, /* [PSX] address family of socket */
ai_socktype: Sock, /* [PSX] socket type */
ai_protocol: Protocol, /* [PSX] protocol of socket */
ai_addrlen: socklen_t, /* [PSX] length of socket address */
ai_canonname: cstring, /* [PSX] canonical name of service location */
ai_addr: ^sockaddr, /* [PSX] binary address */
ai_next: ^addrinfo, /* [PSX] pointer to next in list */
}
when ODIN_OS == .Darwin {
AI_PASSIVE :: 0x00000001
AI_CANONNAME :: 0x00000002
AI_NUMERICHOST :: 0x00000004
AI_NUMERICSERV :: 0x00001000
AI_V4MAPPED :: 0x00000800
AI_ALL :: 0x00000100
AI_ADDRCONFIG :: 0x00000400
NI_NOFQDN :: 0x00000001
NI_NUMERICHOST :: 0x00000002
NI_NAMEREQD :: 0x00000004
NI_NUMERICSERV :: 0x00000008
NI_NUMERICSCOPE :: 0x00000100
NI_DGRAM :: 0x00000010
} else when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD {
AI_PASSIVE :: 0x00000001
AI_CANONNAME :: 0x00000002
AI_NUMERICHOST :: 0x00000004
AI_NUMERICSERV :: 0x00000008
AI_V4MAPPED :: 0x00000800 // NOTE: not implemented on netbsd
AI_ALL :: 0x00000100 // NOTE: not implemented on netbsd
AI_ADDRCONFIG :: 0x00000400
NI_NOFQDN :: 0x00000001
NI_NUMERICHOST :: 0x00000002
NI_NAMEREQD :: 0x00000004
NI_NUMERICSERV :: 0x00000008
NI_NUMERICSCOPE :: 0x00000010
NI_DGRAM :: 0x00000020
} else when ODIN_OS == .OpenBSD {
AI_PASSIVE :: 1
AI_CANONNAME :: 2
AI_NUMERICHOST :: 4
AI_NUMERICSERV :: 16
AI_V4MAPPED :: 0x00000800 // NOTE: not implemented
AI_ALL :: 0x00000100 // NOTE: not implemented
AI_ADDRCONFIG :: 64
NI_NOFQDN :: 4
NI_NUMERICHOST :: 1
NI_NAMEREQD :: 8
NI_NUMERICSERV :: 2
NI_NUMERICSCOPE :: 32
NI_DGRAM :: 16
}
when ODIN_OS == .OpenBSD {
EAI_AGAIN :: -3
EAI_BADFLAGS :: -1
EAI_FAIL :: -4
EAI_FAMILY :: -6
EAI_MEMORY :: -10
EAI_NONAME :: -2
EAI_SERVICE :: -8
EAI_SOCKTYPE :: -7
EAI_SYSTEM :: -11
EAI_OVERFLOW :: -14
} else {
EAI_AGAIN :: 2
EAI_BADFLAGS :: 3
EAI_FAIL :: 4
EAI_FAMILY :: 5
EAI_MEMORY :: 6
EAI_NONAME :: 8
EAI_SERVICE :: 9
EAI_SOCKTYPE :: 10
EAI_SYSTEM :: 11
EAI_OVERFLOW :: 14
}
}else {
#panic("posix is unimplemented for the current target")
}

View File

@@ -0,0 +1,199 @@
package posix
import "core:c"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// netinet/in.h - Internet address family
foreign lib {
in6addr_any: in6_addr
in6addr_loopback: in6_addr
}
in_port_t :: u16be
in_addr_t :: u32be
INET_ADDRSTRLEN :: 16
INET6_ADDRSTRLEN :: 46
Protocol :: enum c.int {
IP = IPPROTO_IP,
ICMP = IPPROTO_ICMP,
IPV6 = IPPROTO_IPV6,
RAW = IPPROTO_RAW,
TCP = IPPROTO_TCP,
UDP = IPPROTO_UDP,
}
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
in_addr :: struct {
s_addr: in_addr_t, /* [PSX] big endian address */
}
in6_addr :: struct {
using _: struct #raw_union {
s6_addr: [16]c.uint8_t, /* [PSX] big endian address */
__u6_addr16: [8]c.uint16_t,
__u6_addr32: [4]c.uint32_t,
},
}
sockaddr_in :: struct {
sin_len: c.uint8_t,
sin_family: sa_family_t, /* [PSX] AF_INET (but a smaller size) */
sin_port: in_port_t, /* [PSX] port number */
sin_addr: in_addr, /* [PSX] IP address */
sin_zero: [8]c.char,
}
sockaddr_in6 :: struct {
sin6_len: c.uint8_t,
sin6_family: sa_family_t, /* [PSX] AF_INET6 (but a smaller size) */
sin6_port: in_port_t, /* [PSX] port number */
sin6_flowinfo: c.uint32_t, /* [PSX] IPv6 traffic class and flow information */
sin6_addr: in6_addr, /* [PSX] IPv6 address */
sin6_scope_id: c.uint32_t, /* [PSX] set of interfaces for a scope */
}
ipv6_mreq :: struct {
ipv6mr_multiaddr: in6_addr, /* [PSX] IPv6 multicast address */
ipv6mr_interface: c.uint, /* [PSX] interface index */
}
IPPROTO_IP :: 0
IPPROTO_ICMP :: 1
IPPROTO_IPV6 :: 41
IPPROTO_RAW :: 255
IPPROTO_TCP :: 6
IPPROTO_UDP :: 17
INADDR_ANY :: 0x00000000
INADDR_BROADCAST :: 0xFFFFFFFF
IPV6_JOIN_GROUP :: 12
IPV6_LEAVE_GROUP :: 13
IPV6_MULTICAST_HOPS :: 10
IPV6_MULTICAST_IF :: 9
IPV6_MULTICAST_LOOP :: 11
IPV6_UNICAST_HOPS :: 4
IPV6_V6ONLY :: 27
IN6_IS_ADDR_UNSPECIFIED :: #force_inline proc "contextless" (a: in6_addr) -> b32 {
return a.s6_addr == 0
}
IN6_IS_ADDR_LOOPBACK :: #force_inline proc "contextless" (a: in6_addr) -> b32 {
a := a
return (
(^c.uint32_t)(&a.s6_addr[0])^ == 0 &&
(^c.uint32_t)(&a.s6_addr[4])^ == 0 &&
(^c.uint32_t)(&a.s6_addr[8])^ == 0 &&
(^u32be)(&a.s6_addr[12])^ == 1 \
)
}
IN6_IS_ADDR_MULTICAST :: #force_inline proc "contextless" (a: in6_addr) -> b32 {
return a.s6_addr[0] == 0xff
}
IN6_IS_ADDR_LINKLOCAL :: #force_inline proc "contextless" (a: in6_addr) -> b32 {
return a.s6_addr[0] == 0xfe && a.s6_addr[1] & 0xc0 == 0x80
}
IN6_IS_ADDR_SITELOCAL :: #force_inline proc "contextless" (a: in6_addr) -> b32 {
return a.s6_addr[0] == 0xfe && a.s6_addr[1] & 0xc0 == 0xc0
}
IN6_IS_ADDR_V4MAPPED :: #force_inline proc "contextless" (a: in6_addr) -> b32 {
a := a
return (
(^c.uint32_t)(&a.s6_addr[0])^ == 0 &&
(^c.uint32_t)(&a.s6_addr[4])^ == 0 &&
(^u32be)(&a.s6_addr[8])^ == 0x0000ffff \
)
}
IN6_IS_ADDR_V4COMPAT :: #force_inline proc "contextless" (a: in6_addr) -> b32 {
a := a
return (
(^c.uint32_t)(&a.s6_addr[0])^ == 0 &&
(^c.uint32_t)(&a.s6_addr[4])^ == 0 &&
(^c.uint32_t)(&a.s6_addr[8])^ == 0 &&
(^c.uint32_t)(&a.s6_addr[12])^ != 0 &&
(^u32be)(&a.s6_addr[12])^ != 1 \
)
}
@(private)
__IPV6_ADDR_SCOPE_NODELOCAL :: 0x01
@(private)
__IPV6_ADDR_SCOPE_LINKLOCAL :: 0x02
@(private)
__IPV6_ADDR_SCOPE_SITELOCAL :: 0x05
@(private)
__IPV6_ADDR_SCOPE_ORGLOCAL :: 0x08
@(private)
__IPV6_ADDR_SCOPE_GLOBAL :: 0x0e
@(private)
IPV6_ADDR_MC_FLAGS :: #force_inline proc "contextless" (a: in6_addr) -> c.uint8_t {
return a.s6_addr[1] & 0xf0
}
@(private)
IPV6_ADDR_MC_FLAGS_TRANSIENT :: 0x10
@(private)
IPV6_ADDR_MC_FLAGS_PREFIX :: 0x20
@(private)
IPV6_ADDR_MC_FLAGS_UNICAST_BASED :: IPV6_ADDR_MC_FLAGS_TRANSIENT | IPV6_ADDR_MC_FLAGS_PREFIX
@(private)
__IPV6_ADDR_MC_SCOPE :: #force_inline proc "contextless" (a: in6_addr) -> c.uint8_t {
return a.s6_addr[1] & 0x0f
}
IN6_IS_ADDR_MC_NODELOCAL :: #force_inline proc "contextless" (a: in6_addr) -> b32 {
return (
IN6_IS_ADDR_MULTICAST(a) &&
(__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_NODELOCAL) \
)
}
IN6_IS_ADDR_MC_LINKLOCAL :: #force_inline proc "contextless" (a: in6_addr) -> b32 {
return (
IN6_IS_ADDR_MULTICAST(a) &&
(IPV6_ADDR_MC_FLAGS(a) != IPV6_ADDR_MC_FLAGS_UNICAST_BASED) &&
(__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_LINKLOCAL) \
)
}
IN6_IS_ADDR_MC_SITELOCAL :: #force_inline proc "contextless" (a: in6_addr) -> b32 {
return (
IN6_IS_ADDR_MULTICAST(a) &&
(__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_SITELOCAL) \
)
}
IN6_IS_ADDR_MC_ORGLOCAL :: #force_inline proc "contextless" (a: in6_addr) -> b32 {
return (
IN6_IS_ADDR_MULTICAST(a) &&
(__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_ORGLOCAL) \
)
}
IN6_IS_ADDR_MC_GLOBAL :: #force_inline proc "contextless" (a: in6_addr) -> b32 {
return (
IN6_IS_ADDR_MULTICAST(a) &&
(__IPV6_ADDR_MC_SCOPE(a) == __IPV6_ADDR_SCOPE_GLOBAL) \
)
}
} else {
#panic("posix is unimplemented for the current target")
}

View File

@@ -0,0 +1,11 @@
package posix
// netinet/tcp.h - definitions for the Internet Transmission Control Protocol (TCP)
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
TCP_NODELAY :: 0x01
} else {
#panic("posix is unimplemented for the current target")
}

78
core/sys/posix/poll.odin Normal file
View File

@@ -0,0 +1,78 @@
package posix
import "base:intrinsics"
import "core:c"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// poll.h - definitions for the poll() function
foreign lib {
/*
For each pointer in fds, poll() shall examine the given descriptor for the events.
poll will identify on which descriptors writes or reads can be done.
Returns: -1 (setting errno) on failure, 0 on timeout, the amount of fds that have been changed on success.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/poll.html ]]
*/
poll :: proc(fds: [^]pollfd, nfds: nfds_t, timeout: c.int) -> c.int ---
}
nfds_t :: c.uint
Poll_Event_Bits :: enum c.short {
// Data other than high-priority data may be read without blocking.
IN = log2(POLLIN),
// Normal data may be read without blocking.
RDNORM = log2(POLLRDNORM),
// Priority data may be read without blocking.
RDBAND = log2(POLLRDBAND),
// High priority data may be read without blocking.
PRI = log2(POLLPRI),
// Normal data may be written without blocking.
OUT = log2(POLLOUT),
// Equivalent to POLLOUT.
WRNORM = log2(POLLWRNORM),
// Priority data may be written.
WRBAND = log2(POLLWRBAND),
// An error has occurred (revents only).
ERR = log2(POLLERR),
// Device hsa been disconnected (revents only).
HUP = log2(POLLHUP),
// Invalid fd member (revents only).
NVAL = log2(POLLNVAL),
}
Poll_Event :: bit_set[Poll_Event_Bits; c.short]
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
pollfd :: struct {
fd: FD, /* [PSX] the following descriptor being polled */
events: Poll_Event, /* [PSX] the input event flags */
revents: Poll_Event, /* [PSX] the output event flags */
}
POLLIN :: 0x0001
POLLRDNORM :: 0x0040
POLLRDBAND :: 0x0080
POLLPRI :: 0x0002
POLLOUT :: 0x0004
POLLWRNORM :: POLLOUT
POLLWRBAND :: 0x0100
POLLERR :: 0x0008
POLLHUP :: 0x0010
POLLNVAL :: 0x0020
} else {
#panic("posix is unimplemented for the current target")
}

70
core/sys/posix/posix.odin Normal file
View File

@@ -0,0 +1,70 @@
/*
Bindings for most POSIX APIs.
APIs that have been left out are due to not being useful,
being fully replaced (and better) by other Odin packages,
or when one of the targets hasn't implemented the API or option.
The struct fields that are cross-platform are documented with `[PSX]`.
Accessing these fields on one target should be the same on others.
Other fields are implementation specific.
Most macros have been reimplemented in Odin with inlined functions.
Unimplemented headers:
- aio.h
- complex.h | See `core:c/libc` and our own complex types
- cpio.h
- ctype.h | See `core:c/libc` for most of it
- ndbm.h
- fenv.h
- float.h
- fmtmsg.h
- ftw.h
- semaphore.h | See `core:sync`
- inttypes.h | See `core:c`
- iso646.h | Impossible
- math.h | See `core:c/libc`
- mqueue.h | Targets don't seem to have implemented it
- regex.h | See `core:regex`
- search.h | Not useful in Odin
- spawn.h | Use `fork`, `execve`, etc.
- stdarg.h | See `core:c/libc`
- stdint.h | See `core:c`
- stropts.h
- syslog.h
- pthread.h | Only the actual threads API is bound, see `core:sync` for synchronization primitives
- string.h | Most of this is not useful in Odin, only a select few symbols are bound
- tar.h
- tgmath.h
- trace.h
- wchar.h
- wctype.h
*/
package posix
import "base:intrinsics"
import "core:c"
result :: enum c.int {
// Use `errno` and `strerror` for more information.
FAIL = -1,
// Operation succeeded.
OK = 0,
}
FD :: distinct c.int
@(private)
log2 :: intrinsics.constant_log2
when ODIN_OS == .Darwin && ODIN_ARCH == .amd64 {
@(private)
INODE_SUFFIX :: "$INODE64"
} else {
@(private)
INODE_SUFFIX :: ""
}

518
core/sys/posix/pthread.odin Normal file
View File

@@ -0,0 +1,518 @@
package posix
import "core:c"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD {
foreign import lib "system:pthread"
} else {
foreign import lib "system:c"
}
// pthread.h - threads
// NOTE: mutexes, rwlock, condition variables, once and barriers are left out in favour of `core:sync`.
foreign lib {
/*
Initializes a thread attributes object.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_init.html ]]
*/
pthread_attr_init :: proc(attr: ^pthread_attr_t) -> Errno ---
/*
Destroys a thread attributes object.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_init.html ]]
*/
pthread_attr_destroy :: proc(attr: ^pthread_attr_t) -> Errno ---
/*
The detachstate attribute controls whether the thread is created in a detached state.
If the thread is created detached, then use of the ID of the newly created thread is an error.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_getdetachstate.html ]]
*/
pthread_attr_getdetachstate :: proc(attr: ^pthread_attr_t, detachstate: ^Detach_State) -> Errno ---
/*
The detachstate attribute controls whether the thread is created in a detached state.
If the thread is created detached, then use of the ID of the newly created thread is an error.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_getdetachstate.html ]]
*/
pthread_attr_setdetachstate :: proc(attr: ^pthread_attr_t, detachstate: Detach_State) -> Errno ---
/*
The guardsize attribute controls the size of the guard area for the created thread's stack.
The guardsize attribute provides protection against overflow of the stack pointer.
If a thread's stack is created with guard protection, the implementation allocates extra memory
at the overflow end of the stack as a buffer against stack overflow of the stack pointer.
If an application overflows into this buffer an error shall result (possibly in a SIGSEGV signal being delivered to the thread).
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_setguardsize.html ]]
*/
pthread_attr_getguardsize :: proc(attr: ^pthread_attr_t, guardsize: ^c.size_t) -> Errno ---
/*
The guardsize attribute controls the size of the guard area for the created thread's stack.
The guardsize attribute provides protection against overflow of the stack pointer.
If a thread's stack is created with guard protection, the implementation allocates extra memory
at the overflow end of the stack as a buffer against stack overflow of the stack pointer.
If an application overflows into this buffer an error shall result (possibly in a SIGSEGV signal being delivered to the thread).
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_setguardsize.html ]]
*/
pthread_attr_setguardsize :: proc(attr: ^pthread_attr_t, guardsize: c.size_t) -> Errno ---
/*
When the attributes objects are used by pthread_create(), the inheritsched attribute determines
how the other scheduling attributes of the created thread shall be set.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_setinheritsched.html ]]
*/
pthread_attr_getinheritsched :: proc(attr: ^pthread_attr_t, inheritsched: ^Inherit_Sched) -> Errno ---
/*
When the attributes objects are used by pthread_create(), the inheritsched attribute determines
how the other scheduling attributes of the created thread shall be set.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_setinheritsched.html ]]
*/
pthread_attr_setinheritsched :: proc(attr: ^pthread_attr_t, inheritsched: Inherit_Sched) -> Errno ---
/*
Gets the scheduling param.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_setschedparam.html ]]
*/
pthread_attr_getschedparam :: proc(attr: ^pthread_attr_t, param: ^sched_param) -> Errno ---
/*
Sets the scheduling param.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_setschedparam.html ]]
*/
pthread_attr_setschedparam :: proc(attr: ^pthread_attr_t, param: ^sched_param) -> Errno ---
/*
Gets the scheduling poicy.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_getschedpolicy.html ]]
*/
pthread_attr_getschedpolicy :: proc(attr: ^pthread_attr_t, policy: ^Sched_Policy) -> Errno ---
/*
Sets the scheduling poicy.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_getschedpolicy.html ]]
*/
pthread_attr_setschedpolicy :: proc(attr: ^pthread_attr_t, policy: Sched_Policy) -> Errno ---
/*
Gets the contention scope.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_getscope.html ]]
*/
pthread_attr_getscope :: proc(attr: ^pthread_attr_t, contentionscope: ^Thread_Scope) -> Errno ---
/*
Sets the contention scope.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_getscope.html ]]
*/
pthread_attr_setscope :: proc(attr: ^pthread_attr_t, contentionscope: ^Thread_Scope) -> Errno ---
/*
Get the area of storage to be used for the created thread's stack.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_getstack.html ]]
*/
pthread_attr_getstack :: proc(attr: ^pthread_attr_t, stackaddr: ^[^]byte, stacksize: ^c.size_t) -> Errno ---
/*
Specify the area of storage to be used for the created thread's stack.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_getstack.html ]]
*/
pthread_attr_setstack :: proc(attr: ^pthread_attr_t, stackaddr: [^]byte, stacksize: c.size_t) -> Errno ---
/*
Gets the stack size.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_getstacksize.html ]]
*/
pthread_attr_getstacksize :: proc(attr: ^pthread_attr_t, stacksize: ^c.size_t) -> Errno ---
/*
Sets the stack size.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_attr_getstacksize.html ]]
*/
pthread_attr_setstacksize :: proc(attr: ^pthread_attr_t, stacksize: c.size_t) -> Errno ---
/*
Register fork handlers to be called before and after fork().
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_atfork.html ]]
*/
pthread_atfork :: proc(prepare: proc "c" (), parent: proc "c" (), child: proc "c" ()) -> Errno ---
/*
Cancel the execution of a thread.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_cancel.html ]]
*/
pthread_cancel :: proc(thread: pthread_t) -> Errno ---
/*
Creates a new thread with the given attributes.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_create.html ]]
*/
pthread_create :: proc(
thread: ^pthread_t,
attr: ^pthread_attr_t,
start_routine: proc "c" (arg: rawptr) -> rawptr,
arg: rawptr,
) -> Errno ---
/*
Indicate that storage for the thread can be reclaimed when the thread terminates.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_detach.html ]]
*/
pthread_detach :: proc(thread: pthread_t) -> Errno ---
/*
Compare thread IDs.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_equal.html ]]
*/
pthread_equal :: proc(t1: pthread_t, t2: pthread_t) -> b32 ---
/*
Terminates the calling thread and make the given value available to any successfull join calls.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_exit.html ]]
*/
pthread_exit :: proc(value_ptr: rawptr) -> ! ---
/*
Gets the current concurrency hint.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_getconcurrency.html ]]
*/
pthread_getconcurrency :: proc() -> c.int ---
/*
Sets the current desired concurrency hint.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_getconcurrency.html ]]
*/
pthread_setconcurrency :: proc(new_level: c.int) -> Errno ---
/*
Access a thread CPU-time clock.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_getcpuclockid.html ]]
*/
pthread_getcpuclockid :: proc(thread_id: pthread_t, clock_id: ^clockid_t) -> Errno ---
/*
Gets the scheduling policy and parameters.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_getschedparam.html ]]
*/
pthread_getschedparam :: proc(thread: pthread_t, policy: ^Sched_Policy, param: ^sched_param) -> Errno ---
/*
Sets the scheduling policy and parameters.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_getschedparam.html ]]
*/
pthread_setschedparam :: proc(thread: pthread_t, policy: Sched_Policy, param: ^sched_param) -> Errno ---
/*
Creates a thread-specific data key visible to all threads in the process.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_key_create.html ]]
*/
pthread_key_create :: proc(key: ^pthread_key_t, destructor: proc "c" (value: rawptr) = nil) -> Errno ---
/*
Deletes a thread-specific data key visible to all threads in the process.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_key_delete.html ]]
*/
pthread_key_delete :: proc(key: pthread_key_t) -> Errno ---
/*
Returns the value currently bound to the specified key on behalf of the calling thread.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_getspecific.html ]]
*/
pthread_getspecific :: proc(key: pthread_key_t) -> rawptr ---
/*
Sets the value currently bound to the specified key on behalf of the calling thread.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_getspecific.html ]]
*/
pthread_setspecific :: proc(key: pthread_key_t, value: rawptr) -> Errno ---
/*
Suspends execution of the calling thread until the target thread terminates.
Example:
ar: [10_000]i32
sb1 := ar[:5_000]
sb2 := ar[5_000:]
th1, th2: posix.pthread_t
posix.pthread_create(&th1, nil, incer, &sb1)
posix.pthread_create(&th2, nil, incer, &sb2)
posix.pthread_join(th1)
posix.pthread_join(th2)
incer :: proc "c" (arg: rawptr) -> rawptr {
sb := (^[]i32)(arg)
for &val in sb {
val += 1
}
return nil
}
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_join.html ]]
*/
pthread_join :: proc(thread: pthread_t, value_ptr: ^rawptr = nil) -> Errno ---
/*
Get the calling thread ID.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_self.html ]]
*/
pthread_self :: proc() -> pthread_t ---
/*
Atomically set the calling thread's cancelability and return the previous value.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_setcancelstate.html ]]
*/
pthread_setcancelstate :: proc(state: Cancel_State, oldstate: ^Cancel_State) -> Errno ---
/*
Atomically set the calling thread's cancel type and return the previous value.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_setcancelstate.html ]]
*/
pthread_setcanceltype :: proc(type: Cancel_Type, oldtype: ^Cancel_Type) -> Errno ---
/*
Creates a cancellation point in the calling thread.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_testcancel.html ]]
*/
pthread_testcancel :: proc() ---
/*
Sets the scheduling priority for the thread given.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_setschedprio.html ]]
*/
pthread_setschedprio :: proc(thread: pthread_t, prio: c.int) -> Errno ---
}
Detach_State :: enum c.int {
// Causes all threads to be in the joinable state.
CREATE_JOINABLE = PTHREAD_CREATE_JOINABLE,
// Causes all threads to be in the detached state.
CREATE_DETACHED = PTHREAD_CREATE_DETACHED,
}
Inherit_Sched :: enum c.int {
// Threads inherit from the creating thread.
INHERIT_SCHED = PTHREAD_INHERIT_SCHED,
// Threads scheduling shall be set to the corresponding values from the attributes object.
EXPLICIT_SCHED = PTHREAD_EXPLICIT_SCHED,
}
Thread_Scope :: enum c.int {
// System scheduling contention scope.
SYSTEM = PTHREAD_SCOPE_SYSTEM,
// Process scheduling contention scope.
PROCESS = PTHREAD_SCOPE_PROCESS,
}
Cancel_State :: enum c.int {
ENABLE = PTHREAD_CANCEL_ENABLE,
DISABLE = PTHREAD_CANCEL_DISABLE,
}
Cancel_Type :: enum c.int {
DEFERRED = PTHREAD_CANCEL_DEFERRED,
ASYNCHRONOUS = PTHREAD_CANCEL_ASYNCHRONOUS,
}
when ODIN_OS == .Darwin {
PTHREAD_CANCEL_ASYNCHRONOUS :: 0x00
PTHREAD_CANCEL_DEFERRED :: 0x02
PTHREAD_CANCEL_DISABLE :: 0x00
PTHREAD_CANCEL_ENABLE :: 0x01
PTHREAD_CANCELED :: rawptr(uintptr(1))
PTHREAD_CREATE_DETACHED :: 2
PTHREAD_CREATE_JOINABLE :: 1
PTHREAD_EXPLICIT_SCHED :: 2
PTHREAD_INHERIT_SCHED :: 1
PTHREAD_PRIO_INHERIT :: 1
PTHREAD_PRIO_NONE :: 0
PTHREAD_PRIO_PROTECT :: 2
PTHREAD_PROCESS_SHARED :: 1
PTHREAD_PROCESS_PRIVATE :: 2
PTHREAD_SCOPE_PROCESS :: 2
PTHREAD_SCOPE_SYSTEM :: 1
pthread_t :: distinct u64
pthread_attr_t :: struct {
__sig: c.long,
__opaque: [56]c.char,
}
pthread_key_t :: distinct c.ulong
sched_param :: struct {
sched_priority: c.int, /* [PSX] process or thread execution scheduling priority */
_: [4]c.char,
}
} else when ODIN_OS == .FreeBSD {
PTHREAD_CANCEL_ASYNCHRONOUS :: 0x02
PTHREAD_CANCEL_DEFERRED :: 0x00
PTHREAD_CANCEL_DISABLE :: 0x01
PTHREAD_CANCEL_ENABLE :: 0x00
PTHREAD_CANCELED :: rawptr(uintptr(1))
PTHREAD_CREATE_DETACHED :: 1
PTHREAD_CREATE_JOINABLE :: 0
PTHREAD_EXPLICIT_SCHED :: 0
PTHREAD_INHERIT_SCHED :: 4
PTHREAD_PRIO_INHERIT :: 1
PTHREAD_PRIO_NONE :: 0
PTHREAD_PRIO_PROTECT :: 2
PTHREAD_PROCESS_SHARED :: 0
PTHREAD_PROCESS_PRIVATE :: 1
PTHREAD_SCOPE_PROCESS :: 0
PTHREAD_SCOPE_SYSTEM :: 2
pthread_t :: distinct u64
pthread_attr_t :: distinct rawptr
pthread_key_t :: distinct c.int
sched_param :: struct {
sched_priority: c.int, /* [PSX] process or thread execution scheduling priority */
}
} else when ODIN_OS == .NetBSD {
PTHREAD_CANCEL_ASYNCHRONOUS :: 1
PTHREAD_CANCEL_DEFERRED :: 0
PTHREAD_CANCEL_DISABLE :: 1
PTHREAD_CANCEL_ENABLE :: 0
PTHREAD_CANCELED :: rawptr(uintptr(1))
PTHREAD_CREATE_DETACHED :: 1
PTHREAD_CREATE_JOINABLE :: 0
PTHREAD_EXPLICIT_SCHED :: 1
PTHREAD_INHERIT_SCHED :: 0
PTHREAD_PRIO_INHERIT :: 1
PTHREAD_PRIO_NONE :: 0
PTHREAD_PRIO_PROTECT :: 2
PTHREAD_PROCESS_SHARED :: 1
PTHREAD_PROCESS_PRIVATE :: 0
PTHREAD_SCOPE_PROCESS :: 0
PTHREAD_SCOPE_SYSTEM :: 1
pthread_t :: distinct rawptr
pthread_attr_t :: struct {
pta_magic: c.uint,
pta_flags: c.int,
pta_private: rawptr,
}
pthread_key_t :: distinct c.int
sched_param :: struct {
sched_priority: c.int, /* [PSX] process or thread execution scheduling priority */
}
} else when ODIN_OS == .OpenBSD {
PTHREAD_CANCEL_ASYNCHRONOUS :: 2
PTHREAD_CANCEL_DEFERRED :: 0
PTHREAD_CANCEL_DISABLE :: 1
PTHREAD_CANCEL_ENABLE :: 0
PTHREAD_CANCELED :: rawptr(uintptr(1))
PTHREAD_CREATE_DETACHED :: 0x1
PTHREAD_CREATE_JOINABLE :: 0
PTHREAD_EXPLICIT_SCHED :: 0
PTHREAD_INHERIT_SCHED :: 0x4
PTHREAD_PRIO_INHERIT :: 1
PTHREAD_PRIO_NONE :: 0
PTHREAD_PRIO_PROTECT :: 2
PTHREAD_PROCESS_SHARED :: 0
PTHREAD_PROCESS_PRIVATE :: 1
PTHREAD_SCOPE_PROCESS :: 0
PTHREAD_SCOPE_SYSTEM :: 0x2
pthread_t :: distinct rawptr
pthread_attr_t :: distinct rawptr
pthread_key_t :: distinct c.int
sched_param :: struct {
sched_priority: c.int, /* [PSX] process or thread execution scheduling priority */
}
} else {
#panic("posix is unimplemented for the current target")
}

168
core/sys/posix/pwd.odin Normal file
View File

@@ -0,0 +1,168 @@
package posix
import "core:c"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// pwd.h - password structure
foreign lib {
/*
Rewinds the user database so that the next getpwent() returns the first entry.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setpwent.html ]]
*/
setpwent :: proc() ---
/*
Returns the current entry in the user database.
Returns: nil (setting errno) on error, nil (not setting errno) on success.
Example:
posix.setpwent()
defer posix.endpwent()
for e := posix.getpwent(); e != nil; e = posix.getpwent() {
fmt.println(e)
}
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setpwent.html ]]
*/
@(link_name=LGETPWENT)
getpwent :: proc() -> ^passwd ---
/*
Closes the user database.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setpwent.html ]]
*/
endpwent :: proc() ---
/*
Searches the database for an entry with a matching name.
Returns: nil (setting errno) on error, nil (not setting errno) on success.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwnam.html ]]
*/
@(link_name=LGETPWNAM)
getpwnam :: proc(name: cstring) -> ^passwd ---
/*
Searches the database for an entry with a matching name.
Populating the pwd fields and using the buffer to allocate strings into.
Setting result to nil on failure and to the address of pwd otherwise.
ERANGE will be returned if there is not enough space in buffer.
sysconf(_SC_GETPW_R_SIZE_MAX) can be called for the suggested size of this buffer, note that it could return -1.
Example:
length := posix.sysconf(._GETPW_R_SIZE_MAX)
length = length == -1 ? 1024 : length
buffer: [dynamic]byte
defer delete(buffer)
result: posix.passwd
resultp: ^posix.passwd
errno: posix.Errno
for {
if err := resize(&buffer, length); err != nil {
fmt.panicf("allocation failure: %v", err)
}
errno = posix.getpwnam_r("root", &result, raw_data(buffer), len(buffer), &resultp)
if errno != .ERANGE {
break
}
}
if errno != .NONE {
panic(string(posix.strerror(errno)))
}
fmt.println(result)
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwnam.html ]]
*/
@(link_name=LGETPWNAMR)
getpwnam_r :: proc(name: cstring, pwd: ^passwd, buffer: [^]byte, bufsize: c.size_t, result: ^^passwd) -> Errno ---
/*
Searches the database for an entry with a matching uid.
Returns: nil (setting errno) on error, nil (not setting errno) on success.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwuid.html ]]
*/
@(link_name=LGETPWUID)
getpwuid :: proc(uid: uid_t) -> ^passwd ---
/*
Searches the database for an entry with a matching uid.
Populating the pwd fields and using the buffer to allocate strings into.
Setting result to nil on failure and to the address of pwd otherwise.
ERANGE will be returned if there is not enough space in buffer.
sysconf(_SC_GETPW_R_SIZE_MAX) can be called for the suggested size of this buffer, note that it could return -1.
See the example for getpwnam_r.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwuid_r.html ]]
*/
@(link_name=LGETPWUIDR)
getpwuid_r :: proc(uid: uid_t, pwd: ^passwd, buffer: [^]byte, bufsize: c.size_t, result: ^^passwd) -> Errno ---
}
when ODIN_OS == .NetBSD {
@(private) LGETPWENT :: "__getpwent50"
@(private) LGETPWNAM :: "__getpwnam50"
@(private) LGETPWNAMR :: "__getpwnam_r50"
@(private) LGETPWUID :: "__getpwuid50"
@(private) LGETPWUIDR :: "__getpwuid_r50"
} else {
@(private) LGETPWENT :: "getpwent"
@(private) LGETPWNAM :: "getpwnam"
@(private) LGETPWNAMR :: "getpwnam_r"
@(private) LGETPWUID :: "getpwuid"
@(private) LGETPWUIDR :: "getpwuid_r"
}
when ODIN_OS == .Darwin || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
passwd :: struct {
pw_name: cstring, /* [PSX] user name */
pw_passwd: cstring, /* encrypted password */
pw_uid: uid_t, /* [PSX] user uid */
pw_gid: gid_t, /* [PSX] user gid */
pw_change: time_t, /* password change time */
pw_class: cstring, /* user access class */
pw_gecos: cstring, /* Honeywell login info */
pw_dir: cstring, /* [PSX] home directory */
pw_shell: cstring, /* [PSX] default shell */
pw_expire: time_t, /* account expiration */
}
} else when ODIN_OS == .FreeBSD {
passwd :: struct {
pw_name: cstring, /* [PSX] user name */
pw_passwd: cstring, /* encrypted password */
pw_uid: uid_t, /* [PSX] user uid */
pw_gid: gid_t, /* [PSX] user gid */
pw_change: time_t, /* password change time */
pw_class: cstring, /* user access class */
pw_gecos: cstring, /* Honeywell login info */
pw_dir: cstring, /* [PSX] home directory */
pw_shell: cstring, /* [PSX] default shell */
pw_expire: time_t, /* account expiration */
pw_fields: c.int,
}
} else {
#panic("posix is unimplemented for the current target")
}

105
core/sys/posix/sched.odin Normal file
View File

@@ -0,0 +1,105 @@
package posix
import "core:c"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// sched.h - execution scheduling
foreign lib {
/*
Returns the minimum for the given scheduling policy.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sched_get_priority_max.html ]]
*/
sched_get_priority_max :: proc(policy: Sched_Policy) -> c.int ---
/*
Returns the maximum for the given scheduling policy.
Returns: -1 (setting errno) on failure, the maximum on success
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sched_get_priority_max.html ]]
*/
sched_get_priority_min :: proc(policy: Sched_Policy) -> c.int ---
/*
Forces the running thread to relinquish the processor until it again becomes the head of its thread list.
*/
sched_yield :: proc() -> result ---
/* NOTE: unimplemented on darwin (I think?).
/*
Get the scheduling params of a process, pid of 0 will return that of the current process.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sched_getparam.html ]]
*/
sched_getparam :: proc(pid: pid_t, param: ^sched_param) -> result ---
/*
Sets the scheduling parameters of the given process, pid of 0 will set that of the current process.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sched_setparam.html ]]
*/
sched_setparam :: proc(pid: pid_t, param: ^sched_param) -> result ---
/*
Returns the scheduling policy of a process, pid of 0 will return that of the current process.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sched_getscheduler.html ]]
*/
sched_getscheduler :: proc(pid: pid_t) -> Sched_Policy ---
/*
Sets the scheduling policy and parameters of the process, pid 0 will be the current process.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sched_setscheduler.html ]]
*/
sched_setscheduler :: proc(pid: pid_t, policy: Sched_Policy, param: ^sched_param) -> result ---
/*
Updates the timespec structure to contain the current execution time limit for the process.
pid of 0 will return that of the current process.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sched_rr_get_interval.html ]]
*/
sched_rr_get_interval :: proc(pid: pid_t, interval: ^timespec) -> result ---
*/
}
Sched_Policy :: enum c.int {
// Error condition of sched_getscheduler.
ERROR = -1,
// First in-first out (FIFO) scheduling policy.
FIFO = SCHED_FIFO,
// Round robin scheduling policy.
RR = SCHED_RR,
// Another scheduling policy.
OTHER = SCHED_OTHER,
}
when ODIN_OS == .Darwin {
SCHED_FIFO :: 4
SCHED_RR :: 2
// SCHED_SPORADIC :: 3 NOTE: not a thing on freebsd, netbsd and probably others, leaving it out
SCHED_OTHER :: 1
} else when ODIN_OS == .FreeBSD || ODIN_OS == .OpenBSD {
SCHED_FIFO :: 1
SCHED_RR :: 3
SCHED_OTHER :: 2
} else when ODIN_OS == .NetBSD {
SCHED_OTHER :: 0
SCHED_FIFO :: 1
SCHED_RR :: 2
} else {
#panic("posix is unimplemented for the current target")
}

View File

@@ -0,0 +1,58 @@
package posix
import "core:c"
import "core:c/libc"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// setjmp.h - stack environment declarations
foreign lib {
/*
Equivalent to longjmp() but must not touch signals.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/_longjmp.html ]]
*/
_longjmp :: proc(env: ^jmp_buf, val: c.int) -> ! ---
/*
Equivalent to setjmp() but must not touch signals.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/_longjmp.html ]]
*/
_setjmp :: proc(env: ^jmp_buf) -> c.int ---
/*
Equivalent to longjmp() but restores saved signal masks.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/siglongjump.html ]]
*/
@(link_name=LSIGLONGJMP)
siglongjmp :: proc(env: ^sigjmp_buf, val: c.int) -> ! ---
/*
Equivalent to setjmp() but restores saved signal masks.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/siglongjump.html ]]
*/
@(link_name=LSIGSETJMP)
sigsetjmp :: proc(env: ^sigjmp_buf, savemask: b32) -> c.int ---
}
jmp_buf :: libc.jmp_buf
sigjmp_buf :: distinct jmp_buf
longjmp :: libc.longjmp
setjmp :: libc.setjmp
when ODIN_OS == .NetBSD {
@(private) LSIGSETJMP :: "__sigsetjmp14"
@(private) LSIGLONGJMP :: "__siglongjmp14"
} else {
@(private) LSIGSETJMP :: "sigsetjmp"
@(private) LSIGLONGJMP :: "siglongjmp"
}

1131
core/sys/posix/signal.odin Normal file

File diff suppressed because it is too large Load Diff

332
core/sys/posix/stdio.odin Normal file
View File

@@ -0,0 +1,332 @@
package posix
import "core:c"
import "core:c/libc"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// stdio.h - standard buffered input/output
foreign lib {
/*
Generates a string that, when used as a pathname,
refers to the current controlling terminal for the current process.
If s is nil, the returned string might be static and overwritten by subsequent calls or other factors.
If s is not nil, s is assumed len(s) >= L_ctermid.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/ctermid.html ]]
*/
ctermid :: proc(s: [^]byte) -> cstring ---
/*
Equivalent to fprintf but output is written to the file descriptor.
Return: number of bytes written, negative (setting errno) on failure
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/dprintf.html ]]
*/
dprintf :: proc(fildse: FD, format: cstring, #c_vararg args: ..any) -> c.int ---
/*
Equivalent to fprintf but output is written to s, it is the user's responsibility to
ensure there is enough space.
Return: number of bytes written, negative (setting errno) on failure
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/dprintf.html ]]
*/
sprintf :: proc(s: [^]byte, format: cstring, #c_vararg args: ..any) -> c.int ---
/*
Associate a stream with a file descriptor.
Returns: nil (setting errno) on failure, the stream on success
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fdopen.html ]]
*/
fdopen :: proc(fildes: FD, mode: cstring) -> ^FILE ---
/*
Map a stream pointer to a file descriptor.
Returns: the file descriptor or -1 (setting errno) on failure
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fileno.html ]]
*/
fileno :: proc(stream: ^FILE) -> FD ---
/*
Locks a file.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/flockfile.html ]]
*/
flockfile :: proc(file: ^FILE) ---
/*
Tries to lock a file.
Returns: 0 if it could be locked, non-zero if it couldn't
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/flockfile.html ]]
*/
ftrylockfile :: proc(file: ^FILE) -> c.int ---
/*
Unlocks a file.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/flockfile.html ]]
*/
funlockfile :: proc(file: ^FILE) ---
/*
Open a memory buffer stream.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fmemopen.html ]]
*/
fmemopen :: proc(buf: [^]byte, size: c.size_t, mode: cstring) -> ^FILE ---
/*
Reposition a file-position indicator in a stream.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fseeko.html ]]
*/
fseeko :: proc(stream: ^FILE, offset: off_t, whence: Whence) -> result ---
/*
Return the file offset in a stream.
Returns: the current file offset, -1 (setting errno) on error
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/ftello.html ]]
*/
ftello :: proc(^FILE) -> off_t ---
/*
Open a dynamic memory buffer stream.
Returns: nil (setting errno) on failure, the stream on success
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/open_memstream.html ]]
*/
open_memstream :: proc(bufp: ^[^]byte, sizep: ^c.size_t) -> ^FILE ---
/*
Equivalent to getc but unaffected by locks.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getc_unlocked.html ]]
*/
getc_unlocked :: proc(stream: ^FILE) -> c.int ---
/*
Equivalent to getchar but unaffected by locks.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getc_unlocked.html ]]
*/
getchar_unlocked :: proc() -> c.int ---
/*
Equivalent to putc but unaffected by locks.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getc_unlocked.html ]]
*/
putc_unlocked :: proc(ch: c.int, stream: ^FILE) -> c.int ---
/*
Equivalent to putchar but unaffected by locks.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getc_unlocked.html ]]
*/
putchar_unlocked :: proc(ch: c.int) -> c.int ---
/*
Read a delimited record from the stream.
Returns: the number of bytes written or -1 on failure/EOF
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getdelim.html ]]
*/
getdelim :: proc(lineptr: ^cstring, n: ^c.size_t, delimiter: c.int, stream: ^FILE) -> c.ssize_t ---
/*
Read a line delimited record from the stream.
Returns: the number of bytes written or -1 on failure/EOF
Example:
fp := posix.fopen(#file, "r")
if fp == nil {
posix.exit(1)
}
line: cstring
length: uint
for {
read := posix.getline(&line, &length, fp)
if read == -1 do break
posix.printf("Retrieved line of length %zu :\n", read)
posix.printf("%s", line)
}
if posix.ferror(fp) != 0 {
/* handle error */
}
posix.free(rawptr(line))
posix.fclose(fp)
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getdelim.html ]]
*/
getline :: proc(lineptr: ^cstring, n: ^c.size_t, stream: ^FILE) -> c.ssize_t ---
/*
Get a string from the stdin stream.
It is up to the user to make sure s is big enough.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/gets.html ]]
*/
gets :: proc(s: [^]byte) -> cstring ---
/*
Create a name for a temporary file.
Returns: an allocated cstring that needs to be freed, nil on failure
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/tempnam.html ]]
*/
tempnam :: proc(dir: cstring, pfx: cstring) -> cstring ---
/*
Executes the command specified, creating a pipe and returning a pointer to a stream that can
read or write from/to the pipe.
Returns: nil (setting errno) on failure or a pointer to the stream
Example:
fp := posix.popen("ls *", "r")
if fp == nil {
/* Handle error */
}
path: [1024]byte
for posix.fgets(raw_data(path[:]), len(path), fp) != nil {
posix.printf("%s", &path)
}
status := posix.pclose(fp)
if status == -1 {
/* Error reported by pclose() */
} else {
/* Use functions described under wait() to inspect `status` in order
to determine success/failure of the command executed by popen() */
}
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/popen.html ]]
*/
popen :: proc(command: cstring, mode: cstring) -> ^FILE ---
/*
Closes a pipe stream to or from a process.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pclose.html ]]
*/
pclose :: proc(stream: ^FILE) -> c.int ---
/*
Equivalent to rename but relative directories are resolved from their respective fds.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/renameat.html ]]
*/
renameat :: proc(oldfd: FD, old: cstring, newfd: FD, new: cstring) -> result ---
}
clearerr :: libc.clearerr
fclose :: libc.fclose
feof :: libc.feof
ferror :: libc.ferror
fflush :: libc.fflush
fgetc :: libc.fgetc
fgetpos :: libc.fgetpos
fgets :: libc.fgets
fopen :: libc.fopen
fprintf :: libc.fprintf
fputc :: libc.fputc
fread :: libc.fread
freopen :: libc.freopen
fscanf :: libc.fscanf
fseek :: libc.fseek
fsetpos :: libc.fsetpos
ftell :: libc.ftell
fwrite :: libc.fwrite
getc :: libc.getc
getchar :: libc.getchar
perror :: libc.perror
printf :: libc.printf
putc :: libc.puts
putchar :: libc.putchar
puts :: libc.puts
remove :: libc.remove
rename :: libc.rename
rewind :: libc.rewind
scanf :: libc.scanf
setbuf :: libc.setbuf
setvbuf :: libc.setvbuf
snprintf :: libc.snprintf
sscanf :: libc.sscanf
tmpfile :: libc.tmpfile
tmpnam :: libc.tmpnam
vfprintf :: libc.vfprintf
vfscanf :: libc.vfscanf
vprintf :: libc.vprintf
vscanf :: libc.vscanf
vsnprintf :: libc.vsnprintf
vsprintf :: libc.vsprintf
vsscanf :: libc.vsscanf
ungetc :: libc.ungetc
to_stream :: libc.to_stream
Whence :: libc.Whence
FILE :: libc.FILE
fpos_t :: libc.fpos_t
BUFSIZ :: libc.BUFSIZ
_IOFBF :: libc._IOFBF
_IOLBF :: libc._IOLBF
_IONBF :: libc._IONBF
SEEK_CUR :: libc.SEEK_CUR
SEEK_END :: libc.SEEK_END
SEEK_SET :: libc.SEEK_SET
FILENAME_MAX :: libc.FILENAME_MAX
FOPEN_MAX :: libc.FOPEN_MAX
TMP_MAX :: libc.TMP_MAX
EOF :: libc.EOF
stderr := libc.stderr
stdin := libc.stdin
stdout := libc.stdout
when ODIN_OS == .Darwin {
L_ctermid :: 1024
L_tmpnam :: 1024
P_tmpdir :: "/var/tmp/"
} else when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
L_ctermid :: 1024
L_tmpnam :: 1024
P_tmpdir :: "/tmp/"
} else {
#panic("posix is unimplemented for the current target")
}

450
core/sys/posix/stdlib.odin Normal file
View File

@@ -0,0 +1,450 @@
package posix
import "base:intrinsics"
import "core:c"
import "core:c/libc"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// stdlib.h - standard library definitions
atof :: libc.atof
atoi :: libc.atoi
atol :: libc.atol
atoll :: libc.atoll
strtod :: libc.strtod
strtof :: libc.strtof
strtol :: libc.strtol
strtoll :: libc.strtoll
strtoul :: libc.strtoul
strtoull :: libc.strtoull
rand :: libc.rand
srand :: libc.srand
calloc :: libc.calloc
malloc :: libc.malloc
realloc :: libc.realloc
abort :: libc.abort
atexit :: libc.atexit
at_quick_exit :: libc.at_quick_exit
exit :: libc.exit
_Exit :: libc._Exit
getenv :: libc.getenv
quick_exit :: libc.quick_exit
system :: libc.system
bsearch :: libc.bsearch
qsort :: libc.qsort
abs :: libc.abs
labs :: libc.labs
llabs :: libc.llabs
div :: libc.div
ldiv :: libc.ldiv
lldiv :: libc.lldiv
mblen :: libc.mblen
mbtowc :: libc.mbtowc
wctomb :: libc.wctomb
mbstowcs :: libc.mbstowcs
wcstombs :: libc.wcstombs
free :: #force_inline proc(ptr: $T) where intrinsics.type_is_pointer(T) || intrinsics.type_is_multi_pointer(T) || T == cstring {
libc.free(rawptr(ptr))
}
foreign lib {
/*
Takes a pointer to a radix-64 representation, in which the first digit is the least significant,
and return the corresponding long value.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/a64l.html ]]
*/
a64l :: proc(s: cstring) -> c.long ---
/*
The l64a() function shall take a long argument and return a pointer to the corresponding
radix-64 representation.
Returns: a string that may be invalidated by subsequent calls
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/a64l.html ]]
*/
l64a :: proc(value: c.long) -> cstring ---
/*
This family of functions shall generate pseudo-random numbers using a linear congruential algorithm and 48-bit integer arithmetic.
Returns: non-negative, double-precision, floating-point values, uniformly distributed over the interval [0.0,1.0)
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/drand48.html ]]
*/
drand48 :: proc() -> c.double ---
/*
This family of functions shall generate pseudo-random numbers using a linear congruential algorithm and 48-bit integer arithmetic.
Returns: non-negative, double-precision, floating-point values, uniformly distributed over the interval [0.0,1.0)
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/drand48.html ]]
*/
erand48 :: proc(xsubi: ^[3]c.ushort) -> c.double ---
/*
This family of functions shall generate pseudo-random numbers using a linear congruential algorithm and 48-bit integer arithmetic.
Returns: return signed long integers uniformly distributed over the interval [-231,231)
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/drand48.html ]]
*/
mrand48 :: proc() -> c.long ---
/*
This family of functions shall generate pseudo-random numbers using a linear congruential algorithm and 48-bit integer arithmetic.
Returns: return signed long integers uniformly distributed over the interval [-231,231)
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/drand48.html ]]
*/
jrand48 :: proc(xsubi: ^[3]c.ushort) -> c.long ---
/*
This family of functions shall generate pseudo-random numbers using a linear congruential algorithm and 48-bit integer arithmetic.
Returns: non-negative, long integers, uniformly distributed over the interval [0,231)
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/drand48.html ]]
*/
lrand48 :: proc() -> c.long ---
/*
This family of functions shall generate pseudo-random numbers using a linear congruential algorithm and 48-bit integer arithmetic.
Returns: non-negative, long integers, uniformly distributed over the interval [0,231)
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/drand48.html ]]
*/
nrand48 :: proc(xsubi: ^[3]c.ushort) -> c.long ---
/*
This family of functions shall generate pseudo-random numbers using a linear congruential algorithm and 48-bit integer arithmetic.
The srand48(), seed48(), and lcong48() functions are initialization entry points, one of which should be invoked before either drand48(), lrand48(), or mrand48() is called.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/drand48.html ]]
*/
srand48 :: proc(seedval: c.long) ---
/*
This family of functions shall generate pseudo-random numbers using a linear congruential algorithm and 48-bit integer arithmetic.
The srand48(), seed48(), and lcong48() functions are initialization entry points, one of which should be invoked before either drand48(), lrand48(), or mrand48() is called.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/drand48.html ]]
*/
lcong48 :: proc(param: ^[7]c.ushort) ---
/*
This family of functions shall generate pseudo-random numbers using a linear congruential algorithm and 48-bit integer arithmetic.
The srand48(), seed48(), and lcong48() functions are initialization entry points, one of which should be invoked before either drand48(), lrand48(), or mrand48() is called.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/drand48.html ]]
*/
seed48 :: proc(seed16v: ^[3]c.ushort) -> ^[3]c.ushort ---
/*
Parses suboption arguments in a flag argument.
Returns: the index of the matched token string, or -1 if no token strings were matched
Example:
args := runtime.args__
Opt :: enum {
RO,
RW,
NAME,
NIL,
}
token := [Opt]cstring{
.RO = "ro",
.RW = "rw",
.NAME = "name",
.NIL = nil,
}
Options :: struct {
readonly, readwrite: bool,
name: cstring,
}
opts: Options
errfnd: bool
for {
opt := posix.getopt(i32(len(args)), raw_data(args), "o:")
if opt == -1 {
break
}
switch opt {
case 'o':
subopt := posix.optarg
value: cstring
for subopt != "" && !errfnd {
o := posix.getsubopt(&subopt, &token[.RO], &value)
switch Opt(o) {
case .RO: opts.readonly = true
case .RW: opts.readwrite = true
case .NAME:
if value == nil {
fmt.eprintfln("missing value for suboption %s", token[.NAME])
errfnd = true
continue
}
opts.name = value
case .NIL:
fallthrough
case:
fmt.eprintfln("no match found for token: %s", value)
errfnd = true
}
}
if opts.readwrite && opts.readonly {
fmt.eprintfln("Only one of %s and %s can be specified", token[.RO], token[.RW])
errfnd = true
}
case:
errfnd = true
}
}
if errfnd || len(args) == 1 {
fmt.eprintfln("\nUsage: %s -o <suboptstring>", args[0])
fmt.eprintfln("suboptions are 'ro', 'rw', and 'name=<value>'")
posix.exit(1)
}
fmt.println(opts)
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsubopt.html ]]
*/
getsubopt :: proc(optionp: ^cstring, keylistp: [^]cstring, valuep: ^cstring) -> c.int ---
/*
Changes the mode and ownership of the slave pseudo-terminal device associated with its master pseudo-terminal counterpart.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/grantpt.html ]]
*/
grantpt :: proc(fildes: FD) -> result ---
/*
Allows a state array, pointed to by the state argument, to be initialized for future use.
Returns: the previous state array or nil on failure
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/initstate.html ]]
*/
@(link_name=LINITSTATE)
initstate :: proc(seed: c.uint, state: [^]byte, size: c.size_t) -> [^]byte ---
/*
Sets the state array of the random number generator.
Returns: the previous state array or nil on failure
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/initstate.html ]]
*/
setstate :: proc(state: [^]byte) -> [^]byte ---
/*
Use a non-linear additive feedback random-number generator employing a default state array
size of 31 long integers to return successive pseudo-random numbers in the range from 0 to 231-1.
The period of this random-number generator is approximately 16 x (231-1).
The size of the state array determines the period of the random-number generator.
Increasing the state array size shall increase the period.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/initstate.html ]]
*/
random :: proc() -> c.long ---
/*
Initializes the current state array using the value of seed.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/initstate.html ]]
*/
@(link_name=LSRANDOM)
srandom :: proc(seed: c.uint) ---
/*
Creates a directory with a unique name derived from template.
The application shall ensure that the string provided in template is a pathname ending
with at least six trailing 'X' characters.
Returns: nil (setting errno) on failure, template on success
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdtemp.html ]]
*/
mkdtemp :: proc(template: [^]byte) -> cstring ---
/*
Creates a regular file with a unique name derived from template and return a file descriptor
for the file open for reading and writing.
The application shall ensure that the string provided in template is a pathname ending with
at least six trailing 'X' characters.
Returns: -1 (setting errno) on failure, an open file descriptor on success
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdtemp.html ]]
*/
mkstemp :: proc(template: cstring) -> FD ---
/*
Allocates size bytes aligned on a boundary specified by alignment, and shall return a pointer
to the allocated memory in memptr.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_memalign.html ]]
*/
posix_memalign :: proc(memptr: ^[^]byte, alignment: c.size_t, size: c.size_t) -> Errno ---
/*
Establishes a connection between a master device for a pseudo-terminal and a file descriptor.
Returns: -1 (setting errno) on failure, an open file descriptor otherwise
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_openpt.html ]]
*/
posix_openpt :: proc(oflag: O_Flags) -> FD ---
/*
Returns the name of the slave pseudo-terminal device associated with a master pseudo-terminal device.
Returns: nil (setting errno) on failure, the name on success, which may be invalidated on subsequent calls
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/ptsname.html ]]
*/
ptsname :: proc(fildes: FD) -> cstring ---
/*
Unlocks the slave pseudo-terminal device associated with the master to which fildes refers.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlockpt.html ]]
*/
unlockpt :: proc(fildes: FD) -> result ---
/*
Uses the string argument to set environment variable values.
Returns: 0 on success, non-zero (setting errno) on failure
Example:
if posix.putenv("HOME=/usr/home") != 0 {
fmt.panicf("putenv failure: %v", posix.strerror(posix.errno()))
}
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/putenv.html ]]
*/
@(link_name=LPUTENV)
putenv :: proc(string: cstring) -> c.int ---
/*
Updates or add a variable in the environment of the calling process.
Example:
if posix.setenv("HOME", "/usr/home") != .OK {
fmt.panicf("putenv failure: %v", posix.strerror(posix.errno()))
}
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setenv.html ]]
*/
setenv :: proc(envname: cstring, envval: cstring, overwrite: b32) -> result ---
/*
Removes an environment variable from the environment of the calling process.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/unsetenv.html ]]
*/
@(link_name=LUNSETENV)
unsetenv :: proc(name: cstring) -> result ---
/*
Computes a sequence of pseudo-random integers in the range [0, {RAND_MAX}].
(The value of the {RAND_MAX} macro shall be at least 32767.)
If rand_r() is called with the same initial value for the object pointed to by seed and that object is not modified between successive returns and calls to rand_r(), the same sequence shall be generated.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/rand_r.html ]]
*/
rand_r :: proc(seed: ^c.uint) -> c.int ---
/*
Derive, from the pathname file_name, an absolute pathname that resolves to the same directory entry,
whose resolution does not involve '.', '..', or symbolic links.
If resolved_name is not `nil` it should be larger than `PATH_MAX` and the result will use it as a backing buffer.
If resolved_name is `nil` the returned string is allocated by `malloc`.
Returns: `nil` (setting errno) on failure, the "real path" otherwise
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/realpath.html ]]
*/
realpath :: proc(file_name: cstring, resolved_name: [^]byte = nil) -> cstring ---
/*
Provides access to an implementation-defined encoding algorithm.
The argument of setkey() is an array of length 64 bytes containing only the bytes with numerical
value of 0 and 1.
If this string is divided into groups of 8, the low-order bit in each group is ignored; this gives a 56-bit key which is used by the algorithm.
This is the key that shall be used with the algorithm to encode a string block passed to encrypt().
The setkey() function shall not change the setting of errno if successful.
An application wishing to check for error situations should set errno to 0 before calling setkey().
If errno is non-zero on return, an error has occurred.
Example:
key: [64]byte
// set key bytes...
posix.set_errno(.NONE)
posix.setkey(raw_data(key))
if errno := posix.errno(); errno != .NONE {
fmt.panicf("setkey failure: %s", posix.strerror(errno))
}
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setkey.html ]]
*/
setkey :: proc(key: [^]byte) ---
}
EXIT_FAILURE :: libc.EXIT_FAILURE
EXIT_SUCCESS :: libc.EXIT_SUCCESS
RAND_MAX :: libc.RAND_MAX
MB_CUR_MAX :: libc.MB_CUR_MAX
div_t :: libc.div_t
ldiv_t :: libc.ldiv_t
lldiv_t :: libc.lldiv_t
when ODIN_OS == .NetBSD {
@(private) LPUTENV :: "__putenv50"
@(private) LINITSTATE :: "__initstate60"
@(private) LSRANDOM :: "__srandom60"
@(private) LUNSETENV :: "__unsetenv13"
} else {
@(private) LPUTENV :: "putenv"
@(private) LINITSTATE :: "initstate"
@(private) LSRANDOM :: "srandom"
@(private) LUNSETENV :: "unsetenv"
}

View File

@@ -0,0 +1,47 @@
package posix
import "core:c"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// string.h - string operations
// NOTE: most of the symbols in this header are not useful in Odin and have been left out.
foreign lib {
/*
Map the error number to a locale-dependent error message string.
Returns: a string that may be invalidated by subsequent calls
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/strerror.html ]]
*/
@(link_name="strerror")
_strerror :: proc(errnum: Errno) -> cstring ---
/*
Map the error number to a locale-dependent error message string and put it in the buffer.
Returns: ERANGE if the buffer is not big enough
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/strerror_r.html ]]
*/
strerror_r :: proc(errnum: Errno, strerrbuf: [^]byte, buflen: c.size_t) -> Errno ---
/*
Map the signal number to an implementation-defined string.
Returns: a string that may be invalidated by subsequent calls
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/strsignal.html ]]
*/
strsignal :: proc(sig: Signal) -> cstring ---
}
strerror :: #force_inline proc "contextless" (errnum: Maybe(Errno) = nil) -> cstring {
return _strerror(errnum.? or_else errno())
}

View File

@@ -0,0 +1,89 @@
package posix
import "core:c"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// sys/ipc.h = XSI interprocess communication access structure
foreign lib {
/*
Generate an IPC key.
Returns: -1 (setting errno) on failure, the key otherwise
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/ftok.html ]]
*/
ftok :: proc(path: cstring, id: c.int) -> key_t ---
}
IPC_Cmd :: enum c.int {
RMID = IPC_RMID,
SET = IPC_SET,
STAT = IPC_STAT,
}
IPC_Flag_Bits :: enum c.int {
CREAT = log2(IPC_CREAT),
EXCL = log2(IPC_EXCL),
NOWAIT = log2(IPC_NOWAIT),
MSG_NOERROR = log2(MSG_NOERROR),
}
IPC_Flags :: bit_set[IPC_Flag_Bits; c.int]
when ODIN_OS == .Darwin {
key_t :: distinct c.int32_t
ipc_perm :: struct {
uid: uid_t, /* [PSX] owner's user ID */
gid: gid_t, /* [PSX] owner's group ID */
cuid: uid_t, /* [PSX] creator's user ID */
cgid: gid_t, /* [PSX] creator's group ID */
mode: mode_t, /* [PSX] read/write perms */
_seq: c.ushort,
_key: key_t,
}
IPC_CREAT :: 0o01000
IPC_EXCL :: 0o02000
IPC_NOWAIT :: 0o04000
IPC_PRIVATE :: key_t(0)
IPC_RMID :: 0
IPC_SET :: 1
IPC_STAT :: 2
} else when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
key_t :: distinct c.long
ipc_perm :: struct {
cuid: uid_t, /* [PSX] creator's user ID */
cgid: gid_t, /* [PSX] creator's group ID */
uid: uid_t, /* [PSX] owner's user ID */
gid: gid_t, /* [PSX] owner's group ID */
mode: mode_t, /* [PSX] read/write perms */
_seq: c.ushort,
_key: key_t,
}
IPC_CREAT :: 0o01000
IPC_EXCL :: 0o02000
IPC_NOWAIT :: 0o04000
IPC_PRIVATE :: key_t(0)
IPC_RMID :: 0
IPC_SET :: 1
IPC_STAT :: 2
} else {
#panic("posix is unimplemented for the current target")
}

View File

@@ -0,0 +1,229 @@
package posix
import "core:c"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// mman.h - memory management declarations
foreign lib {
/*
Establish a mapping between an address space of a process and a memory object.
Returns: MAP_FAILED (setting errno) on failure, the address in memory otherwise
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/mmap.html ]]
*/
mmap :: proc(
addr: rawptr,
len: c.size_t,
prot: Prot_Flags,
flags: Map_Flags,
fd: FD = -1,
off: off_t = 0,
) -> rawptr ---
/*
Unmaps pages of memory.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/munmap.html ]]
*/
munmap :: proc(addr: rawptr, len: c.size_t) -> result ---
/*
Locks a range of the process address space.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/mlock.html ]]
*/
mlock :: proc(addr: rawptr, len: c.size_t) -> result ---
/*
Unlocks a range of the process address space.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/mlock.html ]]
*/
munlock :: proc(addr: rawptr, len: c.size_t) -> result ---
/*
Locks all pages of the process address space.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/mlockall.html ]]
*/
mlockall :: proc(flags: Lock_Flags) -> result ---
/*
Unlocks all pages of the process address space.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/mlockall.html ]]
*/
munlockall :: proc() -> result ---
/*
Set protection of a memory mapping.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/mprotect.html ]]
*/
mprotect :: proc(addr: rawptr, len: c.size_t, prot: Prot_Flags) -> result ---
/*
Write all modified data to permanent storage locations.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/msync.html ]]
*/
@(link_name=LMSYNC)
msync :: proc(addr: rawptr, len: c.size_t, flags: Sync_Flags) -> result ---
/*
Advise the implementation of expected behavior of the application.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_madvise.html ]]
*/
posix_madvise :: proc(addr: rawptr, len: c.size_t, advice: MAdvice) -> Errno ---
/*
Open a shared memory object.
Returns: -1 (setting errno) on failure, an open file descriptor otherwise
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/shm_open.html ]]
*/
shm_open :: proc(name: cstring, oflag: O_Flags, mode: mode_t) -> FD ---
/*
Removes a shared memory object.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/shm_unlink.html ]]
*/
shm_unlink :: proc(name: cstring) -> result ---
}
#assert(_PROT_NONE == 0)
PROT_NONE :: Prot_Flags{}
Prot_Flag_Bits :: enum c.int {
// Data can be executed.
EXEC = log2(PROT_EXEC),
// Data can be read.
READ = log2(PROT_READ),
// Data can be written.
WRITE = log2(PROT_WRITE),
}
Prot_Flags :: bit_set[Prot_Flag_Bits; c.int]
Map_Flag_Bits :: enum c.int {
// Interpret addr exactly.
FIXED = log2(MAP_FIXED),
// Changes are private.
PRIVATE = log2(MAP_PRIVATE),
// Changes are shared.
SHARED = log2(MAP_SHARED),
}
Map_Flags :: bit_set[Map_Flag_Bits; c.int]
Lock_Flag_Bits :: enum c.int {
// Lock all pages currently mapped into the address space of the process.
CURRENT = log2(MCL_CURRENT),
// Lock all pages that become mapped into the address space of the process in the future,
// when those mappings are established.
FUTURE = log2(MCL_FUTURE),
}
Lock_Flags :: bit_set[Lock_Flag_Bits; c.int]
Sync_Flags_Bits :: enum c.int {
// Perform asynchronous writes.
ASYNC = log2(MS_ASYNC),
// Invalidate cached data.
INVALIDATE = log2(MS_INVALIDATE),
// Perform synchronous writes.
// NOTE: use with `posix.MS_SYNC + { .OTHER_FLAG, .OTHER_FLAG }`, unfortunately can't be in
// this bit set enum because it is 0 on some platforms and a value on others.
// LOCAL = RTLD_LOCAL
// SYNC = MS_SYNC,
_MAX = 31,
}
Sync_Flags :: bit_set[Sync_Flags_Bits; c.int]
MAdvice :: enum c.int {
DONTNEED = POSIX_MADV_DONTNEED,
NORMAL = POSIX_MADV_NORMAL,
RANDOM = POSIX_MADV_RANDOM,
SEQUENTIAL = POSIX_MADV_SEQUENTIAL,
WILLNEED = POSIX_MADV_WILLNEED,
}
when ODIN_OS == .NetBSD {
@(private) LMSYNC :: "__msync13"
} else {
@(private) LMSYNC :: "msync"
}
when ODIN_OS == .Darwin || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
PROT_EXEC :: 0x04
_PROT_NONE :: 0x00
PROT_READ :: 0x01
PROT_WRITE :: 0x02
MAP_FIXED :: 0x0010
MAP_PRIVATE :: 0x0002
MAP_SHARED :: 0x0001
when ODIN_OS == .Darwin {
MS_INVALIDATE :: 0x0002
_MS_SYNC :: 0x0010
} else when ODIN_OS == .NetBSD {
MS_INVALIDATE :: 0x0002
_MS_SYNC :: 0x0004
} else when ODIN_OS == .OpenBSD {
MS_INVALIDATE :: 0x0004
_MS_SYNC :: 0x0002
}
MS_ASYNC :: 0x0001
MS_SYNC :: Sync_Flags{Sync_Flags_Bits(log2(_MS_SYNC))}
MCL_CURRENT :: 0x0001
MCL_FUTURE :: 0x0002
MAP_FAILED :: rawptr(~uintptr(0))
POSIX_MADV_DONTNEED :: 4
POSIX_MADV_NORMAL :: 0
POSIX_MADV_RANDOM :: 1
POSIX_MADV_SEQUENTIAL :: 2
POSIX_MADV_WILLNEED :: 3
} else when ODIN_OS == .FreeBSD {
PROT_EXEC :: 0x04
_PROT_NONE :: 0x00
PROT_READ :: 0x01
PROT_WRITE :: 0x02
MAP_FIXED :: 0x0010
MAP_PRIVATE :: 0x0002
MAP_SHARED :: 0x0001
MS_ASYNC :: 0x0001
MS_INVALIDATE :: 0x0002
MS_SYNC :: Sync_Flags{}
MCL_CURRENT :: 0x0001
MCL_FUTURE :: 0x0002
MAP_FAILED :: rawptr(~uintptr(0))
POSIX_MADV_DONTNEED :: 4
POSIX_MADV_NORMAL :: 0
POSIX_MADV_RANDOM :: 1
POSIX_MADV_SEQUENTIAL :: 2
POSIX_MADV_WILLNEED :: 3
} else {
#panic("posix is unimplemented for the current target")
}

161
core/sys/posix/sys_msg.odin Normal file
View File

@@ -0,0 +1,161 @@
package posix
import "core:c"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// sys/msg.h = XSI message queue structures
foreign lib {
/*
Provides various operation as specified by the given cmd.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/msgctl.html ]]
*/
@(link_name=LMSGCTL)
msgctl :: proc(msqid: FD, cmd: IPC_Cmd, buf: ^msqid_ds) -> result ---
/*
Returns the message queue identifier associated with the argument key.
Returns: -1 (setting errno) on failure, the identifier otherwise
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/msgget.html ]]
*/
msgget :: proc(key: key_t, msgflg: IPC_Flags) -> FD ---
/*
Read a message from the queue.
Returns: -1 (setting errno) on failure, the bytes received otherwise
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/msgrcv.html ]]
*/
msgrcv :: proc(
msgid: FD,
msgp: rawptr,
msgsz: c.size_t,
msgtyp: c.long,
msgflg: IPC_Flags,
) -> c.ssize_t ---
/*
Send a message on the queue.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/msgsnd.html ]]
*/
msgsnd :: proc(msgid: FD, msgp: rawptr, msgsz: c.size_t, msgflg: IPC_Flags) -> result ---
}
when ODIN_OS == .NetBSD {
@(private) LMSGCTL :: "__msgctl50"
} else {
@(private) LMSGCTL :: "msgctl"
}
when ODIN_OS == .Darwin {
msgqnum_t :: distinct c.ulong
msglen_t :: distinct c.ulong
MSG_NOERROR :: 0o10000
// NOTE: this is #pragma pack(4)
msqid_ds :: struct #align(4) {
msg_perm: ipc_perm, /* [PSX] operation permission structure */
msg_first: c.int32_t,
msg_last: c.int32_t,
msg_cbytes: msglen_t,
msg_qnum: msgqnum_t, /* [PSX] number of messages currently on queue */
msg_qbytes: msglen_t, /* [PSX] maximum number of bytes allowed on queue */
msg_lspid: pid_t, /* [PSX] process ID of last msgsnd() */
msg_lrpid: pid_t, /* [PSX] process ID of last msgrcv() */
msg_stime: time_t, /* [PSX] time of last msgsnd() */
msg_pad1: c.int32_t,
using _: struct #align(4) {
msg_rtime: time_t, /* [PSX] time of last msgrcv() */
msg_pad2: c.int32_t,
using _: struct #align(4) {
msg_ctime: time_t, /* [PSX] time of last change */
msg_pad3: c.int32_t,
msg_pad4: [4]c.int32_t,
},
},
}
} else when ODIN_OS == .FreeBSD {
msgqnum_t :: distinct c.ulong
msglen_t :: distinct c.ulong
MSG_NOERROR :: 0o10000
msqid_ds :: struct {
msg_perm: ipc_perm, /* [PSX] operation permission structure */
__msg_first: rawptr,
__msg_last: rawptr,
msg_cbytes: msglen_t,
msg_qnum: msgqnum_t, /* [PSX] number of messages currently on queue */
msg_qbytes: msglen_t, /* [PSX] maximum number of bytes allowed on queue */
msg_lspid: pid_t, /* [PSX] process ID of last msgsnd() */
msg_lrpid: pid_t, /* [PSX] process ID of last msgrcv() */
msg_stime: time_t, /* [PSX] time of last msgsnd() */
msg_rtime: time_t, /* [PSX] time of last msgrcv() */
msg_ctime: time_t, /* [PSX] time of last change */
}
} else when ODIN_OS == .NetBSD {
msgqnum_t :: distinct c.ulong
msglen_t :: distinct c.size_t
MSG_NOERROR :: 0o10000
msqid_ds :: struct {
msg_perm: ipc_perm, /* [PSX] operation permission structure */
msg_qnum: msgqnum_t, /* [PSX] number of messages currently on queue */
msg_qbytes: msglen_t, /* [PSX] maximum number of bytes allowed on queue */
msg_lspid: pid_t, /* [PSX] process ID of last msgsnd() */
msg_lrpid: pid_t, /* [PSX] process ID of last msgrcv() */
msg_stime: time_t, /* [PSX] time of last msgsnd() */
msg_rtime: time_t, /* [PSX] time of last msgrcv() */
msg_ctime: time_t, /* [PSX] time of last change */
_msg_first: rawptr,
_msg_last: rawptr,
_msg_cbytes: msglen_t,
}
} else when ODIN_OS == .OpenBSD {
msgqnum_t :: distinct c.ulong
msglen_t :: distinct c.ulong
MSG_NOERROR :: 0o10000
msqid_ds :: struct {
msg_perm: ipc_perm, /* [PSX] operation permission structure */
__msg_first: rawptr,
__msg_last: rawptr,
msg_cbytes: msglen_t,
msg_qnum: msgqnum_t, /* [PSX] number of messages currently on queue */
msg_qbytes: msglen_t, /* [PSX] maximum number of bytes allowed on queue */
msg_lspid: pid_t, /* [PSX] process ID of last msgsnd() */
msg_lrpid: pid_t, /* [PSX] process ID of last msgrcv() */
msg_stime: time_t, /* [PSX] time of last msgsnd() */
msg_pad1: c.long,
msg_rtime: time_t, /* [PSX] time of last msgrcv() */
msg_pad2: c.long,
msg_ctime: time_t, /* [PSX] time of last change */
msg_pad3: c.long,
msg_pad4: [4]c.long,
}
} else {
#panic("posix is unimplemented for the current target")
}

View File

@@ -0,0 +1,152 @@
package posix
import "core:c"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// sys/resource.h - definitions XSI resource operations
foreign lib {
/*
Gets the nice value of the process, process group or user given.
Note that a nice value can be -1, so checking for an error would mean clearing errno, doing the
call and then checking that this returns -1 and it has an errno.
Returns: -1 (setting errno) on failure, the value otherwise
Example:
pid := posix.getpid()
posix.set_errno(.NONE)
prio := posix.getpriority(.PROCESS, pid)
if err := posix.errno(); prio == -1 && err != .NONE {
// Handle error...
}
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpriority.html ]]
*/
getpriority :: proc(which: Which_Prio, who: id_t) -> c.int ---
/*
Sets the nice value of the process, process group or user given.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpriority.html ]]
*/
setpriority :: proc(which: Which_Prio, who: id_t, value: c.int) -> result ---
/*
Get a resource limit.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getrlimit.html ]]
*/
getrlimit :: proc(resource: Resource, rlp: ^rlimit) -> result ---
/*
Set a resource limit.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getrlimit.html ]]
*/
setrlimit :: proc(resource: Resource, rlp: ^rlimit) -> result ---
/*
Get resource usage.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getrusage.html ]]
*/
@(link_name=LGETRUSAGE)
getrusage :: proc(who: Which_Usage, rusage: ^rusage) -> result ---
}
Which_Prio :: enum c.int {
PROCESS = PRIO_PROCESS,
PGRP = PRIO_PGRP,
USER = PRIO_USER,
}
Which_Usage :: enum c.int {
SELF = RUSAGE_SELF,
CHILDREN = RUSAGE_CHILDREN,
}
Resource :: enum c.int {
// Maximum byte size of a core file that may be created by a process.
CORE = RLIMIT_CORE,
// Maximum amount of CPU time, in seconds, used by a process.
CPU = RLIMIT_CPU,
// Maximum size of data segment of the process, in bytes.
DATA = RLIMIT_DATA,
// Maximum size of a file, in bytes, that may be created by a process.
FSIZE = RLIMIT_FSIZE,
// A number one greater than the maximum value that the system may assign to a newly-created descriptor.
NOFILE = RLIMIT_NOFILE,
// The maximum size of the initial thread's stack, in bytes.
STACK = RLIMIT_STACK,
// Maximum size of total available memory of the process, in bytes.
AS = RLIMIT_AS,
}
when ODIN_OS == .NetBSD {
@(private) LGETRUSAGE :: "__getrusage50"
} else {
@(private) LGETRUSAGE :: "getrusage"
}
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
PRIO_PROCESS :: 0
PRIO_PGRP :: 1
PRIO_USER :: 2
rlim_t :: distinct c.uint64_t
RLIM_INFINITY :: (rlim_t(1) << 63) - 1
RLIM_SAVED_MAX :: RLIM_INFINITY
RLIM_SAVED_CUR :: RLIM_INFINITY
RUSAGE_SELF :: 0
RUSAGE_CHILDREN :: -1
rlimit :: struct {
rlim_cur: rlim_t, /* [PSX] the current (soft) limit */
rlim_max: rlim_t, /* [PSX] the hard limit */
}
rusage :: struct {
ru_utime: timeval, /* [PSX] user time used */
ru_stime: timeval, /* [PSX] system time used */
// Informational aliases for source compatibility with programs
// that need more information than that provided by standards,
// and which do not mind being OS-dependent.
ru_maxrss: c.long, /* max resident set size (PL) */
ru_ixrss: c.long, /* integral shared memory size (NU) */
ru_idrss: c.long, /* integral unshared data (NU) */
ru_isrss: c.long, /* integral unshared stack (NU) */
ru_minflt: c.long, /* page reclaims (NU) */
ru_majflt: c.long, /* page faults (NU) */
ru_nswap: c.long, /* swaps (NU) */
ru_inblock: c.long, /* block input operations (atomic) */
ru_outblock: c.long, /* block output operations (atomic) */
ru_msgsnd: c.long, /* messages sent (atomic) */
ru_msgrcv: c.long, /* messages received (atomic) */
ru_nsignals: c.long, /* signals received (atomic) */
ru_nvcsw: c.long, /* voluntary context switches (atomic) */
ru_nivcsw: c.long, /* involuntary " */
}
RLIMIT_CORE :: 4
RLIMIT_CPU :: 0
RLIMIT_DATA :: 2
RLIMIT_FSIZE :: 1
RLIMIT_NOFILE :: 8
RLIMIT_STACK :: 3
RLIMIT_AS :: 5 when ODIN_OS == .Darwin || ODIN_OS == .OpenBSD else 10
} else {
#panic("posix is unimplemented for the current target")
}

View File

@@ -0,0 +1,120 @@
package posix
import "base:intrinsics"
import "core:c"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// sys/select.h - select types
foreign lib {
/*
Examines the file descriptor sets to see whether some of their descriptors are ready for writing,
or have an exceptional condition pending, respectively.
Returns: -1 (setting errno) on failure, total amount of bits set in the bit masks otherwise
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pselect.html ]]
*/
@(link_name=LPSELECT)
pselect :: proc(
nfds: c.int,
readfds: ^fd_set,
writefds: ^fd_set,
errorfds: ^fd_set,
timeout: ^timespec,
sigmask: ^sigset_t,
) -> c.int ---
/*
Equivalent to pselect() except a more specific timeout resolution (nanoseconds),
does not have a signal mask, and may modify the timeout.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pselect.html ]]
*/
@(link_name=LSELECT)
select :: proc(
nfds: c.int,
readfds: ^fd_set,
writefds: ^fd_set,
errorfds: ^fd_set,
timeout: ^timeval,
) -> c.int ---
}
when ODIN_OS == .NetBSD {
LPSELECT :: "__pselect50"
LSELECT :: "__select50"
} else {
LPSELECT :: "pselect"
LSELECT :: "select"
}
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
suseconds_t :: distinct (c.int32_t when ODIN_OS == .Darwin || ODIN_OS == .NetBSD else c.long)
timeval :: struct {
tv_sec: time_t, /* [PSX] seconds */
tv_usec: suseconds_t, /* [PSX] microseconds */
}
// Maximum number of file descriptors in the fd_set structure.
FD_SETSIZE :: #config(POSIX_FD_SETSIZE, 256 when ODIN_OS == .NetBSD else 1024)
@(private)
__NFDBITS :: size_of(c.int32_t) * 8
// NOTE: this seems correct for FreeBSD but they do use a set backed by the long type themselves (thus the align change).
@(private)
ALIGN :: align_of(c.long) when ODIN_OS == .FreeBSD else align_of(c.int32_t)
fd_set :: struct #align(ALIGN) {
fds_bits: [(FD_SETSIZE / __NFDBITS) when (FD_SETSIZE % __NFDBITS) == 0 else (FD_SETSIZE / __NFDBITS) + 1]c.int32_t,
}
@(private)
__check_fd_set :: #force_inline proc "contextless" (_a: FD, _b: rawptr) -> bool {
if _a < 0 {
set_errno(.EINVAL)
}
if _a >= FD_SETSIZE {
set_errno(.EINVAL)
}
return true
}
FD_CLR :: #force_inline proc "contextless" (_fd: FD, _p: ^fd_set) {
if __check_fd_set(_fd, _p) {
_p.fds_bits[cast(c.ulong)_fd / __NFDBITS] &= ~cast(c.int32_t)((cast(c.ulong)1) << (cast(c.ulong)_fd % __NFDBITS))
}
}
FD_ISSET :: #force_inline proc "contextless" (_fd: FD, _p: ^fd_set) -> bool {
if __check_fd_set(_fd, _p) {
return bool(_p.fds_bits[cast(c.ulong)_fd / __NFDBITS] & cast(c.int32_t)((cast(c.ulong)1) << (cast(c.ulong)_fd % __NFDBITS)))
}
return false
}
FD_SET :: #force_inline proc "contextless" (_fd: FD, _p: ^fd_set) {
if __check_fd_set(_fd, _p) {
_p.fds_bits[cast(c.ulong)_fd / __NFDBITS] |= cast(c.int32_t)((cast(c.ulong)1) << (cast(c.ulong)_fd % __NFDBITS))
}
}
FD_ZERO :: #force_inline proc "contextless" (_p: ^fd_set) {
intrinsics.mem_zero(_p, size_of(fd_set))
}
} else {
#panic("posix is unimplemented for the current target")
}

132
core/sys/posix/sys_sem.odin Normal file
View File

@@ -0,0 +1,132 @@
package posix
import "core:c"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// sys/sem.h - XSI semaphore facility
foreign lib {
/*
Provides various semaphore control operation as specified by cmd.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/semctl.html ]]
*/
@(link_name=LSEMCTL)
semctl :: proc(semid: FD, semnum: c.int, cmd: Sem_Cmd, arg: ^semun = nil) -> c.int ---
/*
Returns the semaphore identifier associated with key.
Returns: -1 (setting errno) on failure, a semaphore file descriptor otherwise
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/semget.html ]]
*/
semget :: proc(key: key_t, nsems: c.int, semflg: IPC_Flags) -> FD ---
/*
Perform atomically a user-defined array of semaphore operations in array order on the set of
semaphores associated with the semaphore identifier specified by the argument semid.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/semop.html ]]
*/
semop :: proc(semid: FD, sops: [^]sembuf, nsops: c.size_t) -> result ---
}
Sem_Cmd :: enum c.int {
// Returns the value of semncnt.
GETNCNT = GETNCNT,
// Returns the value of sempid.
GETPID = GETPID,
// Return the value of semval.
GETVAL = GETVAL,
// Returns the value of semval for each semaphore in the semaphore set.
GETALL = GETALL,
// Returns the value of semzcnt.
GETZCNT = GETZCNT,
// Sets the value of semval to arg.val.
SETVAL = SETVAL,
// Sets the value of semval for each semaphore in the set.
SETALL = SETALL,
}
semun :: struct #raw_union {
val: c.int,
buf: ^semid_ds,
array: [^]c.ushort,
}
when ODIN_OS == .NetBSD {
@(private) LSEMCTL :: "__semctl50"
} else {
@(private) LSEMCTL :: "semctl"
}
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
SEM_UNDO :: 0o10000
GETNCNT :: 3
GETPID :: 4
GETVAL :: 5
GETALL :: 6
GETZCNT :: 7
SETVAL :: 8
SETALL :: 9
when ODIN_OS == .Darwin {
// NOTE: this is #pragma pack(4)
semid_ds :: struct #align(4) {
sem_perm: ipc_perm, /* [PSX] operation permission structure */
sem_base: c.int32_t, /* 32 bit base ptr for semaphore set */
sem_nsems: c.ushort, /* [PSX] number of semaphores in set */
sem_otime: time_t, /* [PSX] last semop() */
sem_pad1: c.int32_t,
using _: struct #align(4) {
sem_ctime: time_t, /* [PSX] last time changed by semctl() */
sem_pad2: c.int32_t,
sem_pad3: [4]c.int32_t,
},
}
} else when ODIN_OS == .FreeBSD {
semid_ds :: struct {
sem_perm: ipc_perm, /* [PSX] operation permission structure */
sem_base: rawptr, /* 32 bit base ptr for semaphore set */
sem_nsems: c.ushort, /* [PSX] number of semaphores in set */
sem_otime: time_t, /* [PSX] last semop() */
sem_ctime: time_t, /* [PSX] last time changed by semctl() */
}
} else when ODIN_OS == .NetBSD {
semid_ds :: struct {
sem_perm: ipc_perm, /* [PSX] operation permission structure */
sem_nsems: c.ushort, /* [PSX] number of semaphores in set */
sem_otime: time_t, /* [PSX] last semop() */
sem_ctime: time_t, /* [PSX] last time changed by semctl() */
_sem_base: rawptr, /* 32 bit base ptr for semaphore set */
}
} else when ODIN_OS == .OpenBSD {
semid_ds :: struct {
sem_perm: ipc_perm, /* [PSX] operation permission structure */
sem_nsems: c.ushort, /* [PSX] number of semaphores in set */
sem_otime: time_t, /* [PSX] last semop() */
sem_pad1: c.long,
sem_ctime: time_t, /* [PSX] last time changed by semctl() */
sem_pad2: c.long,
sem_pad3: [4]c.long,
}
}
sembuf :: struct {
sem_num: c.ushort, /* [PSX] semaphore number */
sem_op: c.short, /* [PSX] semaphore operation */
sem_flg: c.short, /* [PSX] operation flags */
}
} else {
#panic("posix is unimplemented for the current target")
}

146
core/sys/posix/sys_shm.odin Normal file
View File

@@ -0,0 +1,146 @@
package posix
import "core:c"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// sys/shm.h = XSI shared memory facility
foreign lib {
/*
Attaches the shared memory segment associated with the identifier
into the address space of the calling process.
Returns: nil (setting errno) on failure, the address otherwise
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/shmat.html ]]
*/
shmat :: proc(shmid: FD, shmaddr: rawptr, shmflag: SHM_Flags) -> rawptr ---
/*
Provides various shared memory operation as specified by the given cmd.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/shmctl.html ]]
*/
@(link_name=LSHMCTL)
shmctl :: proc(shmid: FD, cmd: IPC_Cmd, buf: ^shmid_ds) -> result ---
/*
Detaches the shared memory segment located at the address specified.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/shmdt.html ]]
*/
shmdt :: proc(shmaddr: rawptr) -> result ---
/*
Returns the shared memory identifier associated with key.
Returns: -1 (setting errno) on failure, the shared memory ID otherwise
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/shmget.html ]]
*/
shmget :: proc(key: key_t, size: c.size_t, shmflag: SHM_Flags) -> FD ---
}
SHM_Flag_Bits :: enum c.int {
RDONLY = log2(SHM_RDONLY),
RND = log2(SHM_RND),
}
SHM_Flags :: bit_set[SHM_Flag_Bits; c.int]
when ODIN_OS == .NetBSD {
@(private) LSHMCTL :: "__shmctl50"
} else {
@(private) LSHMCTL :: "shmctl"
}
when ODIN_OS == .Darwin {
SHM_RDONLY :: 0o10000
SHM_RND :: 0o20000
SHMLBA :: 16 * 1024 when ODIN_ARCH == .arm64 else 4096
shmatt_t :: distinct c.ushort
// NOTE: this is #pragma pack(4)
shmid_ds :: struct #align(4) {
shm_perm: ipc_perm, /* [PSX] operation permission structure */
shm_segsz: c.size_t, /* [PSX] size of segment in bytes */
shm_lpid: pid_t, /* [PSX] process ID of last shared memory operation */
shm_cpid: pid_t, /* [PSX] process ID of creator */
shm_nattch: shmatt_t, /* [PSX] number of current attaches */
using _: struct #align(4) {
shm_atime: time_t, /* [PSX] time of last shmat() */
shm_dtime: time_t, /* [PSX] time of last shmdt() */
shm_ctime: time_t, /* [PSX] time of last change by shmctl() */
shm_internal: rawptr,
},
}
} else when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD {
SHM_RDONLY :: 0o10000
SHM_RND :: 0o20000
SHMLBA :: PAGESIZE
shmatt_t :: distinct c.uint
when ODIN_OS == .FreeBSD {
shmid_ds :: struct {
shm_perm: ipc_perm, /* [PSX] operation permission structure */
shm_segsz: c.size_t, /* [PSX] size of segment in bytes */
shm_lpid: pid_t, /* [PSX] process ID of last shared memory operation */
shm_cpid: pid_t, /* [PSX] process ID of creator */
shm_nattch: shmatt_t, /* [PSX] number of current attaches */
shm_atime: time_t, /* [PSX] time of last shmat() */
shm_dtime: time_t, /* [PSX] time of last shmdt() */
shm_ctime: time_t, /* [PSX] time of last change by shmctl() */
}
} else {
shmid_ds :: struct {
shm_perm: ipc_perm, /* [PSX] operation permission structure */
shm_segsz: c.size_t, /* [PSX] size of segment in bytes */
shm_lpid: pid_t, /* [PSX] process ID of last shared memory operation */
shm_cpid: pid_t, /* [PSX] process ID of creator */
shm_nattch: shmatt_t, /* [PSX] number of current attaches */
shm_atime: time_t, /* [PSX] time of last shmat() */
shm_dtime: time_t, /* [PSX] time of last shmdt() */
shm_ctime: time_t, /* [PSX] time of last change by shmctl() */
_shm_internal: rawptr,
}
}
} else when ODIN_OS == .OpenBSD {
SHM_RDONLY :: 0o10000
SHM_RND :: 0o20000
SHMLBA :: 1 << 12
shmatt_t :: distinct c.short
shmid_ds :: struct {
shm_perm: ipc_perm, /* [PSX] operation permission structure */
shm_segsz: c.int, /* [PSX] size of segment in bytes */
shm_lpid: pid_t, /* [PSX] process ID of last shared memory operation */
shm_cpid: pid_t, /* [PSX] process ID of creator */
shm_nattch: shmatt_t, /* [PSX] number of current attaches */
shm_atime: time_t, /* [PSX] time of last shmat() */
__shm_atimensec: c.long,
shm_dtime: time_t, /* [PSX] time of last shmdt() */
__shm_dtimensec: c.long,
shm_ctime: time_t, /* [PSX] time of last change by shmctl() */
__shm_ctimensec: c.long,
_shm_internal: rawptr,
}
} else {
#panic("posix is unimplemented for the current target")
}

View File

@@ -0,0 +1,495 @@
package posix
import "core:c"
when ODIN_OS == .Darwin {
foreign import libc "system:System.framework"
} else {
foreign import libc "system:c"
}
// sys/socket.h - main sockets header
#assert(Protocol.IP == Protocol(0), "socket() assumes this")
foreign libc {
/*
Creates a socket.
Returns: -1 (setting errno) on failure, file descriptor of socket otherwise
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html ]]
*/
@(link_name=LSOCKET)
socket :: proc(domain: AF, type: Sock, protocol: Protocol = .IP) -> FD ---
/*
Extracts the first connection on the queue of pending connections.
Blocks (if not O_NONBLOCK) if there is no pending connection.
Returns: -1 (setting errno) on failure, file descriptor of accepted socket otherwise
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/accept.html ]]
*/
accept :: proc(socket: FD, address: ^sockaddr, address_len: ^socklen_t) -> FD ---
/*
Assigns a local socket address to the socket.
Example:
sfd := posix.socket(.UNIX, .STREAM)
if sfd == -1 {
/* Handle error */
}
addr: posix.sockaddr_un
addr.sun_family = .UNIX
copy(addr.sun_path[:], "/somepath\x00")
if posix.bind(sfd, (^posix.sockaddr)(&addr), size_of(addr)) != .OK {
/* Handle error */
}
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html ]]
*/
bind :: proc(socket: FD, address: ^sockaddr, address_len: socklen_t) -> result ---
/*
Attempt to make a connection.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html ]]
*/
connect :: proc(socket: FD, address: ^sockaddr, address_len: socklen_t) -> result ---
/*
Get the peer address of the specified socket.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpeername.html ]]
*/
getpeername :: proc(socket: FD, address: ^sockaddr, address_len: ^socklen_t) -> result ---
/*
Get the socket name.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockname.html ]]
*/
getsockname :: proc(socket: FD, address: ^sockaddr, address_len: ^socklen_t) -> result ---
/*
Retrieves the value for the option specified by option_name.
level: either `c.int(posix.Protocol(...))` to specify a protocol level or `posix.SOL_SOCKET`
to specify the socket local level.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockopt.html ]]
*/
getsockopt :: proc(
socket: FD,
level: c.int,
option_name: Sock_Option,
option_value: rawptr,
option_len: ^socklen_t,
) -> result ---
/*
Sets the specified option.
level: either `c.int(posix.Protocol(...))` to specify a protocol level or `posix.SOL_SOCKET`
to specify the socket local level.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setsockopt.html ]]
*/
setsockopt :: proc(
socket: FD,
level: c.int,
option_name: Sock_Option,
option_value: rawptr,
option_len: socklen_t,
) -> result ---
/*
Mark the socket as a socket accepting connections.
backlog provides a hint to limit the number of connections on the listen queue.
Implementation may silently reduce the backlog, additionally `SOMAXCONN` specifies the maximum
an implementation has to support.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/listen.html ]]
*/
listen :: proc(socket: FD, backlog: c.int) -> result ---
/*
Receives a message from a socket.
Blocks (besides with O_NONBLOCK) if there is nothing to receive.
Returns: 0 when the peer shutdown with no more messages, -1 (setting errno) on failure, the amount of bytes received on success
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/recv.html ]]
*/
recv :: proc(socket: FD, buffer: rawptr, length: c.size_t, flags: Msg_Flags) -> c.ssize_t ---
/*
Receives a message from a socket.
Equivalent to recv() but retrieves the source address too.
Returns: 0 when the peer shutdown with no more messages, -1 (setting errno) on failure, the amount of bytes received on success
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvfrom.html ]]
*/
recvfrom :: proc(
socket: FD,
buffer: rawptr,
length: c.size_t,
flags: Msg_Flags,
address: ^sockaddr,
address_len: ^socklen_t,
) -> c.ssize_t ---
/*
Receives a message from a socket.
Returns: 0 when the peer shutdown with no more messages, -1 (setting errno) on failure, the amount of bytes received on success
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvmsg.html ]]
*/
recvmsg :: proc(socket: FD, message: ^msghdr, flags: Msg_Flags) -> c.ssize_t ---
/*
Sends a message on a socket.
Returns: -1 (setting errno) on failure, the amount of bytes received on success
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/send.html ]]
*/
send :: proc(socket: FD, buffer: rawptr, length: c.size_t, flags: Msg_Flags) -> c.ssize_t ---
/*
Sends a message on a socket.
Returns: -1 (setting errno) on failure, the amount of bytes received on success
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendmsg.html ]]
*/
sendmsg :: proc(socket: FD, message: ^msghdr, flags: Msg_Flags) -> c.ssize_t ---
/*
Sends a message on a socket.
If the socket is connectionless, the dest_addr is used to send to.
Returns: -1 (setting errno) on failure, the amount of bytes received on success
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendto.html ]]
*/
sendto :: proc(
socket: FD,
message: rawptr,
length: c.size_t,
flags: Msg_Flags,
dest_addr: ^sockaddr,
dest_len: socklen_t,
) -> c.ssize_t ---
/*
Shuts down a socket end or both.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/shutdown.html ]]
*/
shutdown :: proc(socket: FD, how: Shut) -> result ---
/*
Determine wheter a socket is at the out-of-band mark.
Returns: -1 (setting errno) on failure, 0 if not at the mark, 1 if it is
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sockatmark.html ]]
*/
sockatmark :: proc(socket: FD) -> c.int ---
/*
Create a pair of connected sockets.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/socketpair.html ]]
*/
socketpair :: proc(domain: AF, type: Sock, protocol: Protocol, socket_vector: ^[2]FD) -> result ---
}
AF_UNSPEC :: 0
AF :: enum c.int {
// Unspecified.
UNSPEC = AF_UNSPEC,
// Internet domain sockets for use with IPv4 addresses.
INET = AF_INET,
// Internet domain sockets for use with IPv6 addresses.
INET6 = AF_INET6,
// UNIX domain sockets.
UNIX = AF_UNIX,
}
sa_family_t :: enum _sa_family_t {
// Unspecified.
UNSPEC = AF_UNSPEC,
// Internet domain sockets for use with IPv4 addresses.
INET = AF_INET,
// Internet domain sockets for use with IPv6 addresses.
INET6 = AF_INET6,
// UNIX domain sockets.
UNIX = AF_UNIX,
}
Sock :: enum c.int {
// Datagram socket.
DGRAM = SOCK_DGRAM,
// Raw Protocol Interface.
RAW = SOCK_RAW,
// Sequenced-packet socket.
SEQPACKET = SOCK_SEQPACKET,
// Byte-stream socket.
STREAM = SOCK_STREAM,
}
Shut :: enum c.int {
// Disables further receive operations.
RD = SHUT_RD,
// Disables further send and receive operations.
RDWR = SHUT_RDWR,
// Disables further send operations.
WR = SHUT_WR,
}
Msg_Flag_Bits :: enum c.int {
// Control data truncated.
CTRUNC = log2(MSG_CTRUNC),
// Send without using routing table.
DONTROUTE = log2(MSG_DONTROUTE),
// Terminates a record (if supported by protocol).
EOR = log2(MSG_EOR),
// Out-of-band data.
OOB = log2(MSG_OOB),
// No SIGPIPE is generated when an attempt to send is made on a stream-oriented socket that is
// no longer connected.
NOSIGNAL = log2(MSG_NOSIGNAL),
// Leave received data in queue.
PEEK = log2(MSG_PEEK),
// Normal data truncated.
TRUNC = log2(MSG_TRUNC),
// Attempt to fill the read buffer.
WAITALL = log2(MSG_WAITALL),
}
Msg_Flags :: bit_set[Msg_Flag_Bits; c.int]
Sock_Option :: enum c.int {
// Transmission of broadcast message is supported.
BROADCAST = SO_BROADCAST,
// Debugging information is being recorded.
DEBUG = SO_DEBUG,
// Bypass normal routing.
DONTROUTE = SO_DONTROUTE,
// Socket error status.
ERROR = SO_ERROR,
// Connections are kept alive with periodic messages.
KEEPALIVE = SO_KEEPALIVE,
// Socket lingers on close.
LINGER = SO_LINGER,
// Out-of-band data is transmitted in line.
OOBINLINE = SO_OOBINLINE,
// Receive buffer size.
RCVBUF = SO_RCVBUF,
// Receive low water mark.
RCVLOWAT = SO_RCVLOWAT,
// Receive timeout.
RCVTIMEO = SO_RCVTIMEO,
// Reuse of local addresses is supported.
REUSEADDR = SO_REUSEADDR,
// Send buffer size.
SNDBUF = SO_SNDBUF,
// Send low water mark.
SNDLOWAT = SO_SNDLOWAT,
// Send timeout.
SNDTIMEO = SO_SNDTIMEO,
// Socket type.
TYPE = SO_TYPE,
}
when ODIN_OS == .NetBSD {
@(private) LSOCKET :: "__socket30"
} else {
@(private) LSOCKET :: "socket"
}
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
socklen_t :: distinct c.uint
_sa_family_t :: distinct c.uint8_t
sockaddr :: struct {
sa_len: c.uint8_t, /* total length */
sa_family: sa_family_t, /* [PSX] address family */
sa_data: [14]c.char, /* [PSX] socket address */
}
when ODIN_OS == .OpenBSD {
@(private)
_SS_PAD1SIZE :: 6
@(private)
_SS_PAD2SIZE :: 240
} else {
@(private)
_SS_MAXSIZE :: 128
@(private)
_SS_ALIGNSIZE :: size_of(c.int64_t)
@(private)
_SS_PAD1SIZE :: _SS_ALIGNSIZE - size_of(c.uint8_t) - size_of(sa_family_t)
@(private)
_SS_PAD2SIZE :: _SS_MAXSIZE - size_of(c.uint8_t) - size_of(sa_family_t) - _SS_PAD1SIZE - _SS_ALIGNSIZE
}
sockaddr_storage :: struct {
ss_len: c.uint8_t, /* address length */
ss_family: sa_family_t, /* [PSX] address family */
__ss_pad1: [_SS_PAD1SIZE]c.char,
__ss_align: c.int64_t, /* force structure storage alignment */
__ss_pad2: [_SS_PAD2SIZE]c.char,
}
msghdr :: struct {
msg_name: rawptr, /* [PSX] optional address */
msg_namelen: socklen_t, /* [PSX] size of address */
msg_iov: [^]iovec, /* [PSX] scatter/gather array */
msg_iovlen: c.int, /* [PSX] members in msg_iov */
msg_control: rawptr, /* [PSX] ancillary data */
msg_controllen: socklen_t, /* [PSX] ancillary data buffer length */
msg_flags: Msg_Flags, /* [PSX] flags on received message */
}
cmsghdr :: struct {
cmsg_len: socklen_t, /* [PSX] data byte count, including cmsghdr */
cmsg_level: c.int, /* [PSX] originating protocol */
cmsg_type: c.int, /* [PSX] protocol-specific type */
}
SCM_RIGHTS :: 0x01
@(private)
__ALIGN32 :: #force_inline proc "contextless" (p: uintptr) -> uintptr {
__ALIGNBYTES32 :: size_of(c.uint32_t) - 1
return (p + __ALIGNBYTES32) &~ __ALIGNBYTES32
}
// Returns a pointer to the data array.
CMSG_DATA :: #force_inline proc "contextless" (cmsg: ^cmsghdr) -> [^]c.uchar {
return ([^]c.uchar)(uintptr(cmsg) + __ALIGN32(size_of(cmsghdr)))
}
// Returns a pointer to the next cmsghdr or nil.
CMSG_NXTHDR :: #force_inline proc "contextless" (mhdr: ^msghdr, cmsg: ^cmsghdr) -> ^cmsghdr {
if cmsg == nil {
return CMSG_FIRSTHDR(mhdr)
}
ptr := uintptr(cmsg) + __ALIGN32(uintptr(cmsg.cmsg_len))
if ptr + __ALIGN32(size_of(cmsghdr)) > uintptr(mhdr.msg_control) + uintptr(mhdr.msg_controllen) {
return nil
}
return (^cmsghdr)(ptr)
}
// Returns a pointer to the first cmsghdr or nil.
CMSG_FIRSTHDR :: #force_inline proc "contextless" (mhdr: ^msghdr) -> ^cmsghdr {
if mhdr.msg_controllen >= size_of(cmsghdr) {
return (^cmsghdr)(mhdr.msg_control)
}
return nil
}
linger :: struct {
l_onoff: c.int, /* [PSX] indicates whether linger option is enabled */
l_linger: c.int, /* [PSX] linger time in seconds */
}
SOCK_DGRAM :: 2
SOCK_RAW :: 3
SOCK_SEQPACKET :: 5
SOCK_STREAM :: 1
// Options to be accessed at socket level, not protocol level.
SOL_SOCKET :: 0xffff
SO_ACCEPTCONN :: 0x0002
SO_BROADCAST :: 0x0020
SO_DEBUG :: 0x0001
SO_DONTROUTE :: 0x0010
SO_ERROR :: 0x1007
SO_KEEPALIVE :: 0x0008
SO_OOBINLINE :: 0x0100
SO_RCVBUF :: 0x1002
SO_RCVLOWAT :: 0x1004
SO_REUSEADDR :: 0x0004
SO_SNDBUF :: 0x1001
SO_SNDLOWAT :: 0x1003
SO_TYPE :: 0x1008
when ODIN_OS == .Darwin {
SO_LINGER :: 0x1080
SO_RCVTIMEO :: 0x1006
SO_SNDTIMEO :: 0x1005
} else when ODIN_OS == .FreeBSD {
SO_LINGER :: 0x0080
SO_RCVTIMEO :: 0x1006
SO_SNDTIMEO :: 0x1005
} else when ODIN_OS == .NetBSD {
SO_LINGER :: 0x0080
SO_RCVTIMEO :: 0x100c
SO_SNDTIMEO :: 0x100b
} else when ODIN_OS == .OpenBSD {
SO_LINGER :: 0x0080
SO_RCVTIMEO :: 0x1006
SO_SNDTIMEO :: 0x1005
}
// The maximum backlog queue length for listen().
SOMAXCONN :: 128
MSG_CTRUNC :: 0x20
MSG_DONTROUTE :: 0x4
MSG_EOR :: 0x8
MSG_OOB :: 0x1
MSG_PEEK :: 0x2
MSG_TRUNC :: 0x10
MSG_WAITALL :: 0x40
when ODIN_OS == .Darwin {
MSG_NOSIGNAL :: 0x80000
} else when ODIN_OS == .FreeBSD {
MSG_NOSIGNAL :: 0x00020000
} else when ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
MSG_NOSIGNAL :: 0x0400
}
AF_INET :: 2
AF_UNIX :: 1
when ODIN_OS == .Darwin {
AF_INET6 :: 30
} else when ODIN_OS == .FreeBSD {
AF_INET6 :: 28
} else when ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
AF_INET6 :: 24
}
SHUT_RD :: 0
SHUT_RDWR :: 2
SHUT_WR :: 1
} else {
#panic("posix is unimplemented for the current target")
}

View File

@@ -0,0 +1,432 @@
package posix
import "core:c"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// sys/stat.h - data returned by the stat() function
foreign lib {
/*
Equivalent to either stat or lstat (based on the SYMLINK_NOFOLLOW bit in flags)
but resolves relative paths based on the given fd.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fstatat.html ]]
*/
@(link_name="fstatat" + INODE_SUFFIX)
fstatat :: proc(fd: FD, path: cstring, buf: ^stat_t, flag: AT_Flags) -> result ---
/*
Obtain information about a "file" at the given path.
Follows symbolic links.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fstatat.html ]]
*/
@(link_name=LSTAT)
stat :: proc(path: cstring, buf: ^stat_t) -> result ---
/*
Obtain information about an open file.
Follows symbol links.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fstat.html ]]
*/
@(link_name=LFSTAT)
fstat :: proc(fildes: FD, buf: ^stat_t) -> result ---
/*
Obtain information about a "file" at the given path.
Does not follow symlinks (will stat the symlink itself).
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fstatat.html ]]
*/
@(link_name=LLSTAT)
lstat :: proc(path: cstring, buf: ^stat_t) -> result ---
/*
Change the mode of a file.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/chmod.html ]]
*/
chmod :: proc(path: cstring, mode: mode_t) -> result ---
/*
Equivalent to chmod but takes an open file descriptor.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmod.html ]]
*/
fchmod :: proc(fd: FD, mode: mode_t) -> result ---
/*
Equivalent to chmod but follows (or doesn't) symlinks based on the flag and resolves
relative paths from the given fd.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/chmod.html ]]
*/
fchmodat :: proc(fd: FD, path: cstring, mode: mode_t, flag: AT_Flags) -> result ---
/*
Make a directory.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdir.html ]]
*/
mkdir :: proc(path: cstring, mode: mode_t) -> result ---
/*
Equivalent to mkdir but relative paths are relative to fd.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdir.html ]]
*/
mkdirat :: proc(fd: FD, path: cstring, mode: mode_t) -> result ---
/*
Make a FIFO special file.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkfifo.html ]]
*/
mkfifo :: proc(path: cstring, mode: mode_t) -> result ---
/*
Equivalent to mkfifo but relative paths are relative to fd.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkfifo.html ]]
*/
mkfifoat :: proc(fd: FD, path: cstring, mode: mode_t) -> result ---
/*
Make directory, special file, or regular file.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/mknodat.html ]]
*/
@(link_name=LMKNOD)
mknod :: proc(path: cstring, mode: mode_t, dev: dev_t) -> result ---
/*
Equivalent to mknod but relative paths are relative to fd.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/mknodat.html ]]
*/
mknodat :: proc(fd: FD, path: cstring, mode: mode_t, dev: dev_t) -> result ---
/*
Sets the file access and modification time of the given file descriptor.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html ]]
*/
futimens :: proc(fd: FD, times: ^[2]timespec) -> result ---
/*
Equivalent to futimens.
Relative directories are based on fd.
Symlinks may or may not be followed based on the flags.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html ]]
*/
utimensat :: proc(fd: FD, path: cstring, times: ^[2]timespec, flag: AT_Flags) -> result ---
/*
Set and get the file mode creation flags.
Makes the file mode permissions bits in cmask the new default for the process.
Returns: the previous value
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/umask.html ]]
*/
umask :: proc(cmask: mode_t) -> mode_t ---
}
// Read, write, execute user.
S_IRWXU :: mode_t{ .IRUSR, .IWUSR, .IXUSR }
// Read, write, execute group.
S_IRWXG :: mode_t{ .IRGRP, .IWGRP, .IXGRP }
// Read, write, execute other.
S_IRWXO :: mode_t{ .IROTH, .IWOTH, .IXOTH }
Mode_Bits :: enum c.int {
// File type:
IFCHR = log2(_S_IFCHR), /* Character special */
IFIFO = log2(_S_IFIFO), /* FIFO special */
IFREG = log2(_S_IFREG), /* Regular */
IFDIR = log2(_S_IFDIR), /* Directory */
// Permissions:
IRUSR = log2(_S_IRUSR), /* R for owner */
IWUSR = log2(_S_IWUSR), /* W for owner */
IXUSR = log2(_S_IXUSR), /* X for owner */
IRGRP = log2(_S_IRGRP), /* R for group */
IWGRP = log2(_S_IWGRP), /* W for group */
IXGRP = log2(_S_IXGRP), /* X for group */
IROTH = log2(_S_IROTH), /* R for other */
IWOTH = log2(_S_IWOTH), /* W for other */
IXOTH = log2(_S_IXOTH), /* X for other */
ISUID = log2(_S_ISUID), /* Set user ID on execution */
ISGID = log2(_S_ISGID), /* Set group ID on execution */
ISVXT = log2(_S_ISVTX), /* On directories, restricted deletion flag */
}
mode_t :: bit_set[Mode_Bits; _mode_t]
#assert(size_of(mode_t) == size_of(_mode_t))
S_IFMT :: mode_t{ .IFCHR, .IFREG, .IFDIR, .IFIFO }
S_IFSOCK :: mode_t{ .IFREG, .IFDIR }
S_IFLNK :: mode_t{ .IFREG, .IFCHR }
S_IFBLK :: mode_t{ .IFDIR, .IFCHR }
S_IFIFO :: mode_t{ .IFIFO }
S_IFCHR :: mode_t{ .IFCHR }
S_IFDIR :: mode_t{ .IFDIR }
S_IFREG :: mode_t{ .IFREG }
#assert(_S_IFMT == _S_IFCHR|_S_IFREG|_S_IFDIR|_S_IFIFO)
#assert(_S_IFSOCK == _S_IFREG|_S_IFDIR)
#assert(_S_IFLNK == _S_IFREG|_S_IFCHR)
#assert(_S_IFBLK == _S_IFDIR|_S_IFCHR)
// Test for a block special file.
S_ISBLK :: #force_inline proc "contextless" (m: mode_t) -> bool {
return (m & S_IFMT) == S_IFBLK
}
// Test for a character special file.
S_ISCHR :: #force_inline proc "contextless" (m: mode_t) -> bool {
return (m & S_IFMT) == S_IFCHR
}
// Test for a pipe or FIFO special file.
S_ISFIFO :: #force_inline proc "contextless" (m: mode_t) -> bool {
return (m & S_IFMT) == S_IFIFO
}
// Test for a regular file.
S_ISREG :: #force_inline proc "contextless" (m: mode_t) -> bool {
return (m & S_IFMT) == S_IFREG
}
// Test for a directory.
S_ISDIR :: #force_inline proc "contextless" (m: mode_t) -> bool {
return (m & S_IFMT) == S_IFDIR
}
// Test for a symbolic link.
S_ISLNK :: #force_inline proc "contextless" (m: mode_t) -> bool {
return (m & S_IFMT) == S_IFLNK
}
// Test for a socket.
S_ISSOCK :: #force_inline proc "contextless" (m: mode_t) -> bool {
return (m & S_IFMT) == S_IFSOCK
}
_S_IRWXU :: 0o000700
_S_IRUSR :: 0o000400
_S_IWUSR :: 0o000200
_S_IXUSR :: 0o000100
_S_IRWXG :: 0o000070
_S_IRGRP :: 0o000040
_S_IWGRP :: 0o000020
_S_IXGRP :: 0o000010
_S_IRWXO :: 0o000007
_S_IROTH :: 0o000004
_S_IWOTH :: 0o000002
_S_IXOTH :: 0o000001
_S_ISUID :: 0o004000
_S_ISGID :: 0o002000
_S_ISVTX :: 0o001000
_S_IFBLK :: 0o060000
_S_IFCHR :: 0o020000
_S_IFIFO :: 0o010000
_S_IFREG :: 0o100000
_S_IFDIR :: 0o040000
_S_IFLNK :: 0o120000
_S_IFSOCK :: 0o140000
_S_IFMT :: 0o170000
when ODIN_OS == .NetBSD {
@(private) LSTAT :: "__stat50"
@(private) LFSTAT :: "__fstat50"
@(private) LLSTAT :: "__lstat50"
@(private) LMKNOD :: "__mknod50"
} else {
@(private) LSTAT :: "stat" + INODE_SUFFIX
@(private) LFSTAT :: "fstat" + INODE_SUFFIX
@(private) LLSTAT :: "lstat" + INODE_SUFFIX
@(private) LMKNOD :: "mknod"
}
when ODIN_OS == .Darwin {
dev_t :: distinct c.int32_t
nlink_t :: distinct c.uint16_t
_mode_t :: distinct c.uint16_t
blkcnt_t :: distinct c.int64_t
blksize_t :: distinct c.int32_t
ino_t :: distinct c.uint64_t
stat_t :: struct {
st_dev: dev_t, /* [XSI] ID of device containing file */
st_mode: mode_t, /* [XSI] mode of file */
st_nlink: nlink_t, /* [XSI] number of hard links */
st_ino: ino_t, /* [XSI] file serial number */
st_uid: uid_t, /* [XSI] user ID of the file */
st_gid: gid_t, /* [XSI] group ID of the file */
st_rdev: dev_t, /* [XSI] device ID */
st_atim: timespec, /* [XSI] time of last access */
st_mtim: timespec, /* [XSI] time of last data modification */
st_ctim: timespec, /* [XSI] time of last status change */
st_birthtimespec: timespec, /* time of file creation(birth) */
st_size: off_t, /* [XSI] file size, in bytes */
st_blocks: blkcnt_t, /* [XSI] blocks allocated for file */
st_blksize: blksize_t, /* [XSI] optimal blocksize for I/O */
st_flags: c.uint32_t, /* user defined flags for file */
st_gen: c.uint32_t, /* file generation number */
st_lspare: c.int32_t, /* RESERVED */
st_qspare: [2]c.int64_t, /* RESERVED */
}
UTIME_NOW :: -1
UTIME_OMIT :: -2
} else when ODIN_OS == .FreeBSD {
dev_t :: distinct c.uint64_t
nlink_t :: distinct c.uint64_t
_mode_t :: distinct c.uint16_t
blkcnt_t :: distinct c.int64_t
blksize_t :: distinct c.int32_t
ino_t :: distinct c.uint64_t
when ODIN_ARCH == .i386 {
stat_t :: struct {
st_dev: dev_t, /* [XSI] ID of device containing file */
st_ino: ino_t, /* [XSI] file serial number */
st_nlink: nlink_t, /* [XSI] number of hard links */
st_mode: mode_t, /* [XSI] mode of file */
st_padding0: c.int16_t,
st_uid: uid_t, /* [XSI] user ID of the file */
st_gid: gid_t, /* [XSI] group ID of the file */
st_padding1: c.int32_t,
st_rdev: dev_t, /* [XSI] device ID */
st_atim_ext: c.int32_t,
st_atim: timespec, /* [XSI] time of last access */
st_mtim_ext: c.int32_t,
st_mtim: timespec, /* [XSI] time of last data modification */
st_ctim_ext: c.int32_t,
st_ctim: timespec, /* [XSI] time of last status change */
st_birthtimespec: timespec, /* time of file creation(birth) */
st_size: off_t, /* [XSI] file size, in bytes */
st_blocks: blkcnt_t, /* [XSI] blocks allocated for file */
st_blksize: blksize_t, /* [XSI] optimal blocksize for I/O */
st_flags: c.uint32_t, /* user defined flags for file */
st_gen: c.uint64_t,
st_spare: [10]c.uint64_t,
}
} else {
stat_t :: struct {
st_dev: dev_t, /* [XSI] ID of device containing file */
st_ino: ino_t, /* [XSI] file serial number */
st_nlink: nlink_t, /* [XSI] number of hard links */
st_mode: mode_t, /* [XSI] mode of file */
st_padding0: c.int16_t,
st_uid: uid_t, /* [XSI] user ID of the file */
st_gid: gid_t, /* [XSI] group ID of the file */
st_padding1: c.int32_t,
st_rdev: dev_t, /* [XSI] device ID */
st_atim: timespec, /* [XSI] time of last access */
st_mtim: timespec, /* [XSI] time of last data modification */
st_ctim: timespec, /* [XSI] time of last status change */
st_birthtimespec: timespec, /* time of file creation(birth) */
st_size: off_t, /* [XSI] file size, in bytes */
st_blocks: blkcnt_t, /* [XSI] blocks allocated for file */
st_blksize: blksize_t, /* [XSI] optimal blocksize for I/O */
st_flags: c.uint32_t, /* user defined flags for file */
st_gen: c.uint64_t,
st_spare: [10]c.uint64_t,
}
}
UTIME_NOW :: -1
UTIME_OMIT :: -2
} else when ODIN_OS == .NetBSD {
dev_t :: distinct c.uint64_t
nlink_t :: distinct c.uint32_t
_mode_t :: distinct c.uint32_t
blkcnt_t :: distinct c.int64_t
blksize_t :: distinct c.int32_t
ino_t :: distinct c.uint64_t
stat_t :: struct {
st_dev: dev_t, /* [XSI] ID of device containing file */
st_mode: mode_t, /* [XSI] mode of file */
st_ino: ino_t, /* [XSI] file serial number */
st_nlink: nlink_t, /* [XSI] number of hard links */
st_uid: uid_t, /* [XSI] user ID of the file */
st_gid: gid_t, /* [XSI] group ID of the file */
st_rdev: dev_t, /* [XSI] device ID */
st_atim: timespec, /* [XSI] time of last access */
st_mtim: timespec, /* [XSI] time of last data modification */
st_ctim: timespec, /* [XSI] time of last status change */
st_birthtimespec: timespec, /* time of file creation(birth) */
st_size: off_t, /* [XSI] file size, in bytes */
st_blocks: blkcnt_t, /* [XSI] blocks allocated for file */
st_blksize: blksize_t, /* [XSI] optimal blocksize for I/O */
st_flags: c.uint32_t, /* user defined flags for file */
st_gen: c.uint64_t,
st_spare: [2]c.uint32_t,
}
UTIME_NOW :: (1 << 30) - 1
UTIME_OMIT :: (1 << 30) - 2
} else when ODIN_OS == .OpenBSD {
dev_t :: distinct c.int32_t
nlink_t :: distinct c.uint32_t
_mode_t :: distinct c.uint32_t
blkcnt_t :: distinct c.int64_t
blksize_t :: distinct c.int32_t
ino_t :: distinct c.uint64_t
stat_t :: struct {
st_mode: mode_t, /* [XSI] mode of file */
st_dev: dev_t, /* [XSI] ID of device containing file */
st_ino: ino_t, /* [XSI] file serial number */
st_nlink: nlink_t, /* [XSI] number of hard links */
st_uid: uid_t, /* [XSI] user ID of the file */
st_gid: gid_t, /* [XSI] group ID of the file */
st_rdev: dev_t, /* [XSI] device ID */
st_atim: timespec, /* [XSI] time of last access */
st_mtim: timespec, /* [XSI] time of last data modification */
st_ctim: timespec, /* [XSI] time of last status change */
st_size: off_t, /* [XSI] file size, in bytes */
st_blocks: blkcnt_t, /* [XSI] blocks allocated for file */
st_blksize: blksize_t, /* [XSI] optimal blocksize for I/O */
st_flags: c.uint32_t, /* user defined flags for file */
st_gen: c.int32_t,
st_birthtimespec: timespec,
}
UTIME_NOW :: -2
UTIME_OMIT :: -1
} else {
#panic("posix is unimplemented for the current target")
}

View File

@@ -0,0 +1,135 @@
package posix
import "core:c"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// sys/statvfs.h - VFS File System information structure
foreign lib {
/*
Obtains information about the file system containing the fildes.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/statvfs.html ]]
*/
@(link_name=LFSTATVFS)
fstatvfs :: proc(fildes: FD, buf: ^statvfs_t) -> result ---
/*
Obtains information about the file system containing the file named by path.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/statvfs.html ]]
*/
@(link_name=LSTATVFS)
statvfs :: proc(path: cstring, buf: ^statvfs_t) -> result ---
}
VFS_Flag_Bits :: enum c.ulong {
// Read-only file system.
RDONLY = log2(ST_RDONLY),
// Does not support the semantics of the ST_ISUID and ST_ISGID file mode bits.
NOSUID = log2(ST_NOSUID),
}
VFS_Flags :: bit_set[VFS_Flag_Bits; c.ulong]
when ODIN_OS == .NetBSD {
@(private) LFSTATVFS :: "__fstatvfs90"
@(private) LSTATVFS :: "__statvfs90"
} else {
@(private) LFSTATVFS :: "fstatvfs"
@(private) LSTATVFS :: "statvfs"
}
when ODIN_OS == .Darwin || ODIN_OS == .OpenBSD {
fsblkcnt_t :: distinct c.uint
statvfs_t :: struct {
f_bsize: c.ulong, /* [PSX] file system block size */
f_frsize: c.ulong, /* [PSX] fundamental file system block size */
f_blocks: fsblkcnt_t, /* [PSX] total number of blocks on file system in units of f_frsize */
f_bfree: fsblkcnt_t, /* [PSX] total number of free blocks */
f_bavail: fsblkcnt_t, /* [PSX] number of free blocks available to non-privileged process */
f_files: fsblkcnt_t, /* [PSX] total number of file serial numbers */
f_ffree: fsblkcnt_t, /* [PSX] total number of free file serial numbers */
f_favail: fsblkcnt_t, /* [PSX] number of file serial numbers available to non-privileged process */
f_fsid: c.ulong, /* [PSX] file system ID */
f_flag: VFS_Flags, /* [PSX] bit mask of f_flag values */
f_namemax: c.ulong, /* [PSX] maximum filename length */
}
ST_RDONLY :: 0x00000001
ST_NOSUID :: 0x00000002
} else when ODIN_OS == .FreeBSD {
fsblkcnt_t :: distinct c.uint64_t
statvfs_t :: struct {
f_bavail: fsblkcnt_t, /* [PSX] number of free blocks available to non-privileged process */
f_bfree: fsblkcnt_t, /* [PSX] total number of free blocks */
f_blocks: fsblkcnt_t, /* [PSX] total number of blocks on file system in units of f_frsize */
f_favail: fsblkcnt_t, /* [PSX] number of file serial numbers available to non-privileged process */
f_ffree: fsblkcnt_t, /* [PSX] total number of free file serial numbers */
f_files: fsblkcnt_t, /* [PSX] total number of file serial numbers */
f_bsize: c.ulong, /* [PSX] file system block size */
f_flag: VFS_Flags, /* [PSX] bit mask of f_flag values */
f_frsize: c.ulong, /* [PSX] fundamental file system block size */
f_fsid: c.ulong, /* [PSX] file system ID */
f_namemax: c.ulong, /* [PSX] maximum filename length */
}
ST_RDONLY :: 0x00000001
ST_NOSUID :: 0x00000002
} else when ODIN_OS == .NetBSD {
fsblkcnt_t :: distinct c.uint64_t
@(private)
_VFS_NAMELEN :: 1024
@(private)
fsid_t :: struct {
__fsid_val: [2]c.int,
}
statvfs_t :: struct {
f_flag: VFS_Flags, /* [PSX] bit mask of f_flag values */
f_bsize: c.ulong, /* [PSX] file system block size */
f_frsize: c.ulong, /* [PSX] fundamental file system block size */
f_iosize: c.ulong,
f_blocks: fsblkcnt_t, /* [PSX] total number of blocks on file system in units of f_frsize */
f_bfree: fsblkcnt_t, /* [PSX] total number of free blocks */
f_bavail: fsblkcnt_t, /* [PSX] number of free blocks available to non-privileged process */
f_bresvd: fsblkcnt_t,
f_files: fsblkcnt_t, /* [PSX] total number of file serial numbers */
f_ffree: fsblkcnt_t, /* [PSX] total number of free file serial numbers */
f_favail: fsblkcnt_t, /* [PSX] number of file serial numbers available to non-privileged process */
f_fresvd: fsblkcnt_t,
f_syncreads: c.uint64_t,
f_syncwrites: c.uint64_t,
f_asyncreads: c.uint64_t,
f_asyncwrites: c.uint64_t,
f_fsidx: fsid_t,
f_fsid: c.ulong, /* [PSX] file system ID */
f_namemax: c.ulong, /* [PSX] maximum filename length */
f_owner: uid_t,
f_spare: [4]c.uint64_t,
f_fstypename: [_VFS_NAMELEN]c.char `fmt:"s,0"`,
f_mntonname: [_VFS_NAMELEN]c.char `fmt:"s,0"`,
f_mntfromname: [_VFS_NAMELEN]c.char `fmt:"s,0"`,
f_mntfromlabel: [_VFS_NAMELEN]c.char `fmt:"s,0"`,
}
ST_RDONLY :: 0x00000001
ST_NOSUID :: 0x00000008
} else {
#panic("posix is unimplemented for the current target")
}

View File

@@ -0,0 +1,82 @@
package posix
import "core:c"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// sys/time.h - time types
foreign lib {
/*
Store the current value of timer into value.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getitimer.html ]]
*/
@(link_name=LGETITIMER)
getitimer :: proc(which: ITimer, value: ^itimerval) -> result ---
/*
Set the timer to the value given, and store the previous value in ovalue if it is not nil.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getitimer.html ]]
*/
@(link_name=LSETITIMER)
setitimer :: proc(which: ITimer, value: ^itimerval, ovalue: ^itimerval) -> result ---
/*
Obtains the current time.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/gettimeofday.html ]]
*/
@(link_name=LGETTIMEOFDAY)
gettimeofday :: proc(tp: ^timeval, tzp: rawptr = nil) -> result ---
/*
Sets the access and modification times of the file at the given path.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimes.html ]]
*/
@(link_name=LUTIMES)
utimes :: proc(path: cstring, times: ^[2]timeval) -> result ---
}
ITimer :: enum c.int {
// Decrements in real time.
REAL = ITIMER_REAL,
// Decrements in process virtual time, only when the process is executing.
VIRTUAL = ITIMER_VIRTUAL,
// Decrements both in process virtual time and when the system is running on
// behalf of the process.
PROF = ITIMER_PROF,
}
when ODIN_OS == .NetBSD {
@(private) LGETITIMER :: "__getitimer50"
@(private) LSETITIMER :: "__setitimer50"
@(private) LGETTIMEOFDAY :: "__gettimeofday50"
@(private) LUTIMES :: "__utimes50"
} else {
@(private) LGETITIMER :: "getitimer"
@(private) LSETITIMER :: "setitimer"
@(private) LGETTIMEOFDAY :: "gettimeofday"
@(private) LUTIMES :: "utimes"
}
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
itimerval :: struct {
it_interval: timeval, /* [PSX] timer interval */
it_value: timeval, /* [PSX] current value */
}
ITIMER_REAL :: 0
ITIMER_VIRTUAL :: 1
ITIMER_PROF :: 2
} else {
#panic("posix is unimplemented for the current target")
}

View File

@@ -0,0 +1,38 @@
package posix
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// sys/times.h - file access and modification times structure
foreign lib {
/*
Get time accounting information.
Returns: -1 (setting errno) on failure, the elapsed real time, since an arbitrary point in the past
*/
@(link_name=LTIMES)
times :: proc(buffer: ^tms) -> clock_t ---
}
when ODIN_OS == .NetBSD {
@(private) LTIMES :: "__times13"
} else {
@(private) LTIMES :: "times"
}
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
tms :: struct {
tms_utime: clock_t, /* [PSX] user CPU time */
tms_stime: clock_t, /* [PSX] system CPU time */
tms_cutime: clock_t, /* [PSX] terminated children user CPU time */
tms_cstime: clock_t, /* [PSX] terminated children system CPU time */
}
} else {
#panic("posix is unimplemented for the current target")
}

View File

@@ -0,0 +1,42 @@
package posix
import "core:c"
when ODIN_OS == .Darwin {
foreign import libc "system:System.framework"
} else {
foreign import libc "system:c"
}
// sys/uio.h - definitions for vector I/O operations
foreign libc {
/*
Equivalent to read() but takes a vector of inputs.
iovcnt can be 0..=IOV_MAX in length.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/readv.html ]]
*/
readv :: proc(fildes: FD, iov: [^]iovec, iovcnt: c.int) -> c.ssize_t ---
/*
Equivalent to write() but takes a vector of inputs.
iovcnt can be 0..=IOV_MAX in length.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/readv.html ]]
*/
writev :: proc(fildes: FD, iov: [^]iovec, iovcnt: c.int) -> c.ssize_t ---
}
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
iovec :: struct {
iov_base: rawptr, /* [PSX] base address of I/O memory region */
iov_len: c.size_t, /* [PSX] size of the region iov_base points to */
}
} else {
#panic("posix is unimplemented for the current target")
}

View File

@@ -0,0 +1,17 @@
package posix
import "core:c"
// sys/un.h = definitions for UNIX domain sockets
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
sockaddr_un :: struct {
sun_len: c.uchar, /* sockaddr len including nil */
sun_family: sa_family_t, /* [PSX] address family */
sun_path: [104]c.char, /* [PSX] socket pathname */
}
} else {
#panic("posix is unimplemented for the current target")
}

View File

@@ -0,0 +1,55 @@
package posix
import "core:c"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// sys/utsname.h = system name structure
foreign lib {
/*
Stores information identifying the current system in the given structure.
Returns: non-negative on success, -1 (setting errno) on failure
NOTE: have a look at `core:sys/info` for similar/better system information.
Example:
uname: posix.utsname
posix.uname(&uname)
fmt.printfln("%#v", uname)
Possible Output:
utsname{
sysname = Darwin,
nodename = Laytans-MacBook-Pro.local,
release = 23.5.0,
version = Darwin Kernel Version 23.5.0: Wed May 1 20:16:51 PDT 2024; root:xnu-11331.111.3~1/RELEASE_ARM64_T8103,
machine = arm64,
}
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/uname.html ]]
*/
uname :: proc(uname: ^utsname) -> c.int ---
}
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
@(private)
_SYS_NAMELEN :: 256
utsname :: struct {
sysname: [_SYS_NAMELEN]c.char `fmt:"s,0"`, /* [PSX] name of OS */
nodename: [_SYS_NAMELEN]c.char `fmt:"s,0"`, /* [PSX] name of this network node */
release: [_SYS_NAMELEN]c.char `fmt:"s,0"`, /* [PSX] release level */
version: [_SYS_NAMELEN]c.char `fmt:"s,0"`, /* [PSX] version level */
machine: [_SYS_NAMELEN]c.char `fmt:"s,0"`, /* [PSX] hardware type */
}
} else {
#panic("posix is unimplemented for the current target")
}

View File

@@ -0,0 +1,380 @@
package posix
import "core:c"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// sys/wait.h - declarations for waiting
foreign lib {
/*
Obtains status information pertaining to one of the caller's child processes.
Returns: -1 (setting errno) on failure or signal on calling process, the pid of the process that caused the return otherwise
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html ]]
*/
wait :: proc(stat_loc: ^c.int) -> pid_t ---
/*
Obtains status information pertaining to the given pid specifier.
If pid is -1, status is requested for any child process.
If pid is greater than 0, it specifies the process ID of a single child process.
If pid is 0, it specifies any child process whose process group ID is equal to that of the call.
If pid is < -1, status is requested for any child whose process group ID is the absolute value of pid.
Returns: -1 (setting errno) on failure or signal on calling process, 0 if NOHANG and status is not available, the pid of the process that caused the return otherwise
Example:
// The following example demonstrates the use of waitpid(), fork(), and the macros used to
// interpret the status value returned by waitpid() (and wait()). The code segment creates a
// child process which does some unspecified work. Meanwhile the parent loops performing calls
// to waitpid() to monitor the status of the child. The loop terminates when child termination
// is detected.
child_pid := posix.fork(); switch child_pid {
case -1: // `fork` failed.
panic("fork failed")
case 0: // This is the child.
// Do some work...
case:
for {
status: i32
wpid := posix.waitpid(child_pid, &status, { .UNTRACED, .CONTINUED })
if wpid == -1 {
panic("waitpid failure")
}
switch {
case posix.WIFEXITED(status):
fmt.printfln("child exited, status=%v", posix.WEXITSTATUS(status))
case posix.WIFSIGNALED(status):
fmt.printfln("child killed (signal %v)", posix.WTERMSIG(status))
case posix.WIFSTOPPED(status):
fmt.printfln("child stopped (signal %v", posix.WSTOPSIG(status))
case posix.WIFCONTINUED(status):
fmt.println("child continued")
case:
// Should never happen.
fmt.println("unexpected status (%x)", status)
}
if posix.WIFEXITED(status) || posix.WIFSIGNALED(status) {
break
}
}
}
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html ]]
*/
waitpid :: proc(pid: pid_t, stat_loc: ^c.int, options: Wait_Flags) -> pid_t ---
/*
Obtains status information pertaining to the given idtype_t and id specifier.
Returns: 0 if WNOHANG and no status available, 0 if child changed state, -1 (setting errno) on failure
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/waitid.html ]]
*/
waitid :: proc(idtype: idtype_t, id: id_t, infop: ^siginfo_t, options: Wait_Flags) -> c.int ---
}
// If terminated normally.
WIFEXITED :: #force_inline proc "contextless" (x: c.int) -> bool {
return _WIFEXITED(x)
}
// If WIFEXITED is true, returns the exit status.
WEXITSTATUS :: #force_inline proc "contextless" (x: c.int) -> c.int {
return _WEXITSTATUS(x)
}
// If terminated due to an uncaught signal.
WIFSIGNALED :: #force_inline proc "contextless" (x: c.int) -> bool {
return _WIFSIGNALED(x)
}
// If WIFSIGNALED is true, returns the signal.
WTERMSIG :: #force_inline proc "contextless" (x: c.int) -> Signal {
return _WTERMSIG(x)
}
// If status was returned for a child process that is currently stopped.
WIFSTOPPED :: #force_inline proc "contextless" (x: c.int) -> bool {
return _WIFSTOPPED(x)
}
// If WIFSTOPPED, the signal that caused the child process to stop.
WSTOPSIG :: #force_inline proc "contextless" (x: c.int) -> Signal {
return _WSTOPSIG(x)
}
// If status was returned for a child process that has continued from a job control stop.
WIFCONTINUED :: #force_inline proc "contextless" (x: c.int) -> bool {
return _WIFCONTINUED(x)
}
idtype_t :: enum c.int {
// Wait for any children and `id` is ignored.
P_ALL,
// Wait for any child wiith a process group ID equal to `id`.
P_PID,
// Wait for any child with a process group ID equal to `id`.
P_PGID,
}
Wait_Flag_Bits :: enum c.int {
// Report the status of any continued child process specified by pid whose status has not been
// reported since it continued from a job control stop.
CONTINUED = log2(WCONTINUED),
// Don't suspend execution of the calling thread if status is not immediately available for one
// of the child processes specified by pid.
NOHANG = log2(WNOHANG),
// The status of any child process specified by pid that are stopped, and whose status has not
// yet been reported since they stopped, shall also be reported to the requesting process.
UNTRACED = log2(WUNTRACED),
// Following are only available on `waitid`, not `waitpid`.
// Wait for processes that have exited.
EXITED = log2(WEXITED),
// Keep the process whose status is returned in a waitable state, so it may be waited on again.
NOWAIT = log2(WNOWAIT),
// Children that have stopped upon receipt of a signal, and whose status either hasn't been reported
// or has been reported but that report was called with NOWAIT.
STOPPED = log2(WSTOPPED),
}
Wait_Flags :: bit_set[Wait_Flag_Bits; c.int]
when ODIN_OS == .Darwin {
id_t :: distinct c.uint
WCONTINUED :: 0x00000010
WNOHANG :: 0x00000001
WUNTRACED :: 0x00000002
WEXITED :: 0x00000004
WNOWAIT :: 0x00000020
WSTOPPED :: 0x00000008
@(private)
_WSTATUS :: #force_inline proc "contextless" (x: c.int) -> c.int {
return x & 0o177
}
@(private)
_WSTOPPED :: 0o177
@(private)
_WIFEXITED :: #force_inline proc "contextless" (x: c.int) -> bool {
return _WSTATUS(x) == 0
}
@(private)
_WEXITSTATUS :: #force_inline proc "contextless" (x: c.int) -> c.int {
return x >> 8
}
@(private)
_WIFSIGNALED :: #force_inline proc "contextless" (x: c.int) -> bool {
return _WSTATUS(x) != _WSTOPPED && _WSTATUS(x) != 0
}
@(private)
_WTERMSIG :: #force_inline proc "contextless" (x: c.int) -> Signal {
return Signal(_WSTATUS(x))
}
@(private)
_WIFSTOPPED :: #force_inline proc "contextless" (x: c.int) -> bool {
return _WSTATUS(x) == _WSTOPPED && WSTOPSIG(x) != .SIGCONT
}
@(private)
_WSTOPSIG :: #force_inline proc "contextless" (x: c.int) -> Signal {
return Signal(x >> 8)
}
@(private)
_WIFCONTINUED :: #force_inline proc "contextless" (x: c.int) -> bool {
return _WSTATUS(x) == _WSTOPPED && WSTOPSIG(x) == .SIGCONT
}
} else when ODIN_OS == .FreeBSD {
id_t :: distinct c.int64_t
WCONTINUED :: 4
WNOHANG :: 1
WUNTRACED :: 2
WEXITED :: 16
WNOWAIT :: 8
WSTOPPED :: 2
@(private)
_WSTATUS :: #force_inline proc "contextless" (x: c.int) -> c.int {
return x & 0o177
}
@(private)
_WSTOPPED :: 0o177
@(private)
_WIFEXITED :: #force_inline proc "contextless" (x: c.int) -> bool {
return _WSTATUS(x) == 0
}
@(private)
_WEXITSTATUS :: #force_inline proc "contextless" (x: c.int) -> c.int {
return x >> 8
}
@(private)
_WIFSIGNALED :: #force_inline proc "contextless" (x: c.int) -> bool {
return _WSTATUS(x) != _WSTOPPED && _WSTATUS(x) != 0 && x != c.int(Signal.SIGCONT)
}
@(private)
_WTERMSIG :: #force_inline proc "contextless" (x: c.int) -> Signal {
return Signal(_WSTATUS(x))
}
@(private)
_WIFSTOPPED :: #force_inline proc "contextless" (x: c.int) -> bool {
return _WSTATUS(x) == _WSTOPPED
}
@(private)
_WSTOPSIG :: #force_inline proc "contextless" (x: c.int) -> Signal {
return Signal(x >> 8)
}
@(private)
_WIFCONTINUED :: #force_inline proc "contextless" (x: c.int) -> bool {
return x == c.int(Signal.SIGCONT)
}
} else when ODIN_OS == .NetBSD {
id_t :: distinct c.uint32_t
WCONTINUED :: 0x00000010
WNOHANG :: 0x00000001
WUNTRACED :: 0x00000002
WEXITED :: 0x00000020
WNOWAIT :: 0x00010000
WSTOPPED :: 0x00000002
@(private)
_WSTATUS :: #force_inline proc "contextless" (x: c.int) -> c.int {
return x & 0o177
}
@(private)
_WSTOPPED :: 0o177
@(private)
_WIFEXITED :: #force_inline proc "contextless" (x: c.int) -> bool {
return _WSTATUS(x) == 0
}
@(private)
_WEXITSTATUS :: #force_inline proc "contextless" (x: c.int) -> c.int {
return c.int((c.uint(x) >> 8) & 0xff)
}
@(private)
_WIFSIGNALED :: #force_inline proc "contextless" (x: c.int) -> bool {
return !WIFSTOPPED(x) && !WIFCONTINUED(x) && !WIFEXITED(x)
}
@(private)
_WTERMSIG :: #force_inline proc "contextless" (x: c.int) -> Signal {
return Signal(_WSTATUS(x))
}
@(private)
_WIFSTOPPED :: #force_inline proc "contextless" (x: c.int) -> bool {
return _WSTATUS(x) == _WSTOPPED && !WIFCONTINUED(x)
}
@(private)
_WSTOPSIG :: #force_inline proc "contextless" (x: c.int) -> Signal {
return Signal(c.int((c.uint(x) >> 8) & 0xff))
}
@(private)
_WIFCONTINUED :: #force_inline proc "contextless" (x: c.int) -> bool {
return x == 0xffff
}
} else when ODIN_OS == .OpenBSD {
id_t :: distinct c.uint32_t
WCONTINUED :: 0x00000010
WNOHANG :: 0x00000001
WUNTRACED :: 0x00000002
WEXITED :: 0x00000020
WNOWAIT :: 0x00010000
WSTOPPED :: 0x00000002
@(private)
_WSTATUS :: #force_inline proc "contextless" (x: c.int) -> c.int {
return x & 0o177
}
@(private)
_WSTOPPED :: 0o177
@(private)
_WCONTINUED :: 0o177777
@(private)
_WIFEXITED :: #force_inline proc "contextless" (x: c.int) -> bool {
return _WSTATUS(x) == 0
}
@(private)
_WEXITSTATUS :: #force_inline proc "contextless" (x: c.int) -> c.int {
return (x >> 8) & 0x000000ff
}
@(private)
_WIFSIGNALED :: #force_inline proc "contextless" (x: c.int) -> bool {
return _WSTATUS(x) != _WSTOPPED && _WSTATUS(x) != 0
}
@(private)
_WTERMSIG :: #force_inline proc "contextless" (x: c.int) -> Signal {
return Signal(_WSTATUS(x))
}
@(private)
_WIFSTOPPED :: #force_inline proc "contextless" (x: c.int) -> bool {
return (x & 0xff) == _WSTOPPED
}
@(private)
_WSTOPSIG :: #force_inline proc "contextless" (x: c.int) -> Signal {
return Signal((x >> 8) & 0xff)
}
@(private)
_WIFCONTINUED :: #force_inline proc "contextless" (x: c.int) -> bool {
return (x & _WCONTINUED) == _WCONTINUED
}
} else {
#panic("posix is unimplemented for the current target")
}

475
core/sys/posix/termios.odin Normal file
View File

@@ -0,0 +1,475 @@
package posix
import "core:c"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// termios.h - define values for termios
foreign lib {
/*
Get the input baud rate.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetispeed.html ]]
*/
cfgetispeed :: proc(termios_p: ^termios) -> speed_t ---
/*
Set the input baud rate.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetispeed.html ]]
*/
cfsetispeed :: proc(termios_p: ^termios, rate: speed_t) -> result ---
/*
Get the output baud rate.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetospeed.html ]]
*/
cfgetospeed :: proc(termios_p: ^termios) -> speed_t ---
/*
Set the output baud rate.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetospeed.html ]]
*/
cfsetospeed :: proc(termios_p: ^termios, rate: speed_t) -> result ---
/*
Wait for transmission of output.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcdrain.html ]]
*/
tcdrain :: proc(fildes: FD) -> result ---
/*
Suspend or restart the transmission or reception of data.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcflow.html ]]
*/
tcflow :: proc(fildes: FD, action: TC_Action) -> result ---
/*
Flush non-transmitted output data, non-read input data, or both.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcflush.html ]]
*/
tcflush :: proc(fildes: FD, queue_selector: TC_Queue) -> result ---
/*
Get the parameters associated with the terminal.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetattr.html ]]
*/
tcgetattr :: proc(fildes: FD, termios_p: ^termios) -> result ---
/*
Get the process group ID for the session leader for the controlling terminal.
Returns: -1 (setting errno) on failure, the pid otherwise
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetsid.html ]]
*/
tcgetsid :: proc(fildes: FD) -> pid_t ---
/*
Send a break for a specific duration.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsendbreak.html ]]
*/
tcsendbreak :: proc(fildes: FD, duration: c.int) -> result ---
/*
Set the parameters associated with the terminal.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsetattr.html ]]
*/
tcsetattr :: proc(fildes: FD, optional_actions: c.int, termios_p: ^termios) -> result ---
}
Control_Char :: enum c.int {
VEOF = VEOF,
VEOL = VEOL,
VERASE = VERASE,
VINTR = VINTR,
VKILL = VKILL,
VMIN = VMIN,
VQUIT = VQUIT,
VSTART = VSTART,
VSTOP = VSTOP,
VSUSP = VSUSP,
VTIME = VTIME,
NCCS = NCCS-1,
}
#assert(len(#sparse [Control_Char]cc_t) == NCCS)
CInput_Flag_Bits :: enum tcflag_t {
IGNBRK = log2(IGNBRK), /* ignore BREAK condition */
BRKINT = log2(BRKINT), /* map BREAK to SIGINTR */
IGNPAR = log2(IGNPAR), /* ignore (discard) parity errors */
PARMRK = log2(PARMRK), /* mark parity and framing errors */
INPCK = log2(INPCK), /* enable checking of parity errors */
ISTRIP = log2(ISTRIP), /* strip 8th bit off chars */
INLCR = log2(INLCR), /* map NL into CR */
IGNCR = log2(IGNCR), /* ignore CR */
ICRNL = log2(ICRNL), /* map CR to NL (ala CRMOD) */
IXON = log2(IXON), /* enable output flow control */
IXOFF = log2(IXOFF), /* enable input flow control */
IXANY = log2(IXANY), /* any char will restart after stop */
}
CInput_Flags :: bit_set[CInput_Flag_Bits; tcflag_t]
CLocal_Flag_Bits :: enum tcflag_t {
ECHO = log2(ECHO), /* visual erase for line kill */
ECHOE = log2(ECHOE), /* visually erase chars */
ECHOK = log2(ECHOK), /* echo NL after line kill */
ECHONL = log2(ECHONL), /* echo NL even if ECHO is off */
ICANON = log2(ICANON), /* canonicalize input lines */
IEXTEN = log2(IEXTEN), /* enable DISCARD and LNEXT */
ISIG = log2(ISIG), /* enable signals INTR, QUIT, [D]SUSP */
NOFLSH = log2(NOFLSH), /* don't flush after interrupt */
TOSTOP = log2(TOSTOP), /* stop background jobs from output */
}
CLocal_Flags :: bit_set[CLocal_Flag_Bits; tcflag_t]
CControl_Flag_Bits :: enum tcflag_t {
// CS5 = log2(CS5), /* 5 bits (pseudo) (default) */
CS6 = log2(CS6), /* 6 bits */
CS7 = log2(CS7), /* 7 bits */
CS8 = log2(CS8), /* 8 bits */
CSTOPB = log2(CSTOPB), /* send 2 stop bits */
CREAD = log2(CREAD), /* enable receiver */
PARENB = log2(PARENB), /* parity enable */
PARODD = log2(PARODD), /* odd parity, else even */
HUPCL = log2(HUPCL), /* hang up on last close */
CLOCAL = log2(CLOCAL), /* ignore modem status lines */
}
CControl_Flags :: bit_set[CControl_Flag_Bits; tcflag_t]
// character size mask
CSIZE :: CControl_Flags{ .CS6, .CS7, .CS8 }
COutput_Flag_Bits :: enum tcflag_t {
OPOST = log2(OPOST), /* enable following output processing */
ONLCR = log2(ONLCR), /* map NL to CR-NL (ala CRMOD) */
OCRNL = log2(OCRNL), /* map CR to NL on output */
ONOCR = log2(ONOCR), /* no CR output at column 0 */
ONLRET = log2(ONLRET), /* NL performs CR function */
OFDEL = log2(OFDEL), /* fill is DEL, else NUL */
OFILL = log2(OFILL), /* use fill characters for delay */
// NL0 = log2(NL0), /* \n delay 0 (default) */
NL1 = log2(NL1), /* \n delay 1 */
// CR0 = log2(CR0), /* \r delay 0 (default) */
CR1 = log2(CR1), /* \r delay 1 */
CR2 = log2(CR2), /* \r delay 2 */
CR3 = log2(CR3), /* \r delay 3 */
// TAB0 = log2(TAB0),/* horizontal tab delay 0 (default) */
TAB1 = log2(TAB1), /* horizontal tab delay 1 */
TAB3 = log2(TAB3), /* horizontal tab delay 3 */
// BS0 = log2(BS0), /* \b delay 0 (default) */
BS1 = log2(BS1), /* \b delay 1 */
// VT0 = log2(VT0), /* vertical tab delay 0 (default) */
VT1 = log2(VT1), /* vertical tab delay 1 */
// FF0 = log2(FF0), /* form feed delay 0 (default) */
FF1 = log2(FF1), /* form feed delay 1 */
}
COutput_Flags :: bit_set[COutput_Flag_Bits; tcflag_t]
// \n delay mask
NLDLY :: COutput_Flags{ .NL1, COutput_Flag_Bits(9) }
// \r delay mask
CRDLY :: COutput_Flags{ .CR1, .CR2, .CR3 }
// horizontal tab delay mask
TABDLY :: COutput_Flags{ .TAB1, .TAB3, COutput_Flag_Bits(2) }
// \b delay mask
BSDLY :: COutput_Flags{ .BS1 }
// vertical tab delay mask
VTDLY :: COutput_Flags{ .VT1 }
// form feed delay mask
FFDLY :: COutput_Flags{ .FF1 }
speed_t :: enum _speed_t {
B0 = B0,
B50 = B50,
B75 = B75,
B110 = B110,
B134 = B134,
B150 = B150,
B200 = B200,
B300 = B300,
B600 = B600,
B1200 = B1200,
B1800 = B1800,
B2400 = B2400,
B4800 = B4800,
B9600 = B9600,
B19200 = B19200,
B38400 = B38400,
}
TC_Action :: enum c.int {
TCIOFF = TCIOFF,
TCION = TCION,
TCOOFF = TCOOFF,
TCOON = TCOON,
}
TC_Queue :: enum c.int {
TCIFLUSH = TCIFLUSH,
TCOFLUSH = TCOFLUSH,
TCIOFLUSH = TCIOFLUSH,
}
when ODIN_OS == .Darwin {
cc_t :: distinct c.uchar
_speed_t :: distinct c.ulong
tcflag_t :: distinct c.ulong
termios :: struct {
c_iflag: CInput_Flags, /* [XBD] input flags */
c_oflag: COutput_Flags, /* [XBD] output flags */
c_cflag: CControl_Flags, /* [XBD] control flags */
c_lflag: CLocal_Flags, /* [XBD] local flag */
c_cc: #sparse [Control_Char]cc_t, /* [XBD] control chars */
c_ispeed: speed_t, /* input speed */
c_ospeed: speed_t, /* output speed */
}
NCCS :: 20
VEOF :: 0
VEOL :: 1
VERASE :: 3
VINTR :: 8
VKILL :: 5
VMIN :: 16
VQUIT :: 9
VSTART :: 12
VSTOP :: 13
VSUSP :: 10
VTIME :: 17
IGNBRK :: 0x00000001
BRKINT :: 0x00000002
IGNPAR :: 0x00000004
PARMRK :: 0x00000008
INPCK :: 0x00000010
ISTRIP :: 0x00000020
INLCR :: 0x00000040
IGNCR :: 0x00000080
ICRNL :: 0x00000100
IXON :: 0x00000200
IXOFF :: 0x00000400
IXANY :: 0x00000800
OPOST :: 0x00000001
ONLCR :: 0x00000002
OCRNL :: 0x00000010
ONOCR :: 0x00000020
ONLRET :: 0x00000040
OFDEL :: 0x00020000
OFILL :: 0x00000080
_NLDLY :: 0x00000300
NL0 :: 0x00000000
NL1 :: 0x00000100
_CRDLY :: 0x00003000
CR0 :: 0x00000000
CR1 :: 0x00001000
CR2 :: 0x00002000
CR3 :: 0x00003000
_TABDLY :: 0x00000c04
TAB0 :: 0x00000000
TAB1 :: 0x00000400
TAB3 :: 0x00000800
_BSDLY :: 0x00008000
BS0 :: 0x00000000
BS1 :: 0x00008000
_VTDLY :: 0x00010000
VT0 :: 0x00000000
VT1 :: 0x00010000
_FFDLY :: 0x00004000
FF0 :: 0x00000000
FF1 :: 0x00004000
B0 :: 0
B50 :: 50
B75 :: 75
B110 :: 110
B134 :: 134
B150 :: 150
B200 :: 200
B300 :: 300
B600 :: 600
B1200 :: 1200
B1800 :: 1800
B2400 :: 2400
B4800 :: 4800
B9600 :: 9600
B19200 :: 19200
B38400 :: 38400
_CSIZE :: 0x00000300
CS5 :: 0x00000000
CS6 :: 0x00000100
CS7 :: 0x00000200
CS8 :: 0x00000300
CSTOPB :: 0x00000400
CREAD :: 0x00000800
PARENB :: 0x00001000
PARODD :: 0x00002000
HUPCL :: 0x00004000
CLOCAL :: 0x00008000
ECHO :: 0x00000008
ECHOE :: 0x00000002
ECHOK :: 0x00000004
ECHONL :: 0x00000010
ICANON :: 0x00000100
IEXTEN :: 0x00000400
ISIG :: 0x00000080
NOFLSH :: 0x80000000
TOSTOP :: 0x00400000
TCIFLUSH :: 1
TCOFLUSH :: 2
TCIOFLUSH :: 3
TCIOFF :: 3
TCION :: 4
TCOOFF :: 1
TCOON :: 2
} else when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
cc_t :: distinct c.uchar
_speed_t :: distinct c.uint
tcflag_t :: distinct c.uint
termios :: struct {
c_iflag: CInput_Flags, /* [XBD] input flags */
c_oflag: COutput_Flags, /* [XBD] output flags */
c_cflag: CControl_Flags, /* [XBD] control flags */
c_lflag: CLocal_Flags, /* [XBD] local flag */
c_cc: #sparse [Control_Char]cc_t, /* [XBD] control chars */
c_ispeed: speed_t, /* input speed */
c_ospeed: speed_t, /* output speed */
}
NCCS :: 20
VEOF :: 0
VEOL :: 1
VERASE :: 3
VINTR :: 8
VKILL :: 5
VMIN :: 16
VQUIT :: 9
VSTART :: 12
VSTOP :: 13
VSUSP :: 10
VTIME :: 17
IGNBRK :: 0x00000001
BRKINT :: 0x00000002
IGNPAR :: 0x00000004
PARMRK :: 0x00000008
INPCK :: 0x00000010
ISTRIP :: 0x00000020
INLCR :: 0x00000040
IGNCR :: 0x00000080
ICRNL :: 0x00000100
IXON :: 0x00000200
IXOFF :: 0x00000400
IXANY :: 0x00000800
OPOST :: 0x00000001
ONLCR :: 0x00000002
OCRNL :: 0x00000010
when ODIN_OS == .OpenBSD {
ONOCR :: 0x00000040
ONLRET :: 0x00000080
} else {
ONOCR :: 0x00000020
ONLRET :: 0x00000040
}
OFDEL :: 0x00020000 // NOTE: not in headers
OFILL :: 0x00000080 // NOTE: not in headers
_NLDLY :: 0x00000300 // NOTE: not in headers
NL0 :: 0x00000000 // NOTE: not in headers
NL1 :: 0x00000100 // NOTE: not in headers
_CRDLY :: 0x00003000 // NOTE: not in headers
CR0 :: 0x00000000 // NOTE: not in headers
CR1 :: 0x00001000 // NOTE: not in headers
CR2 :: 0x00002000 // NOTE: not in headers
CR3 :: 0x00003000 // NOTE: not in headers
_TABDLY :: 0x00000004 // NOTE: not in headers (netbsd)
TAB0 :: 0x00000000 // NOTE: not in headers (netbsd)
TAB1 :: 0x00000004 // NOTE: not in headers
TAB3 :: 0x00000004 // NOTE: not in headers (netbsd)
_BSDLY :: 0x00008000 // NOTE: not in headers
BS0 :: 0x00000000 // NOTE: not in headers
BS1 :: 0x00008000 // NOTE: not in headers
_VTDLY :: 0x00010000 // NOTE: not in headers
VT0 :: 0x00000000 // NOTE: not in headers
VT1 :: 0x00010000 // NOTE: not in headers
_FFDLY :: 0x00004000 // NOTE: not in headers
FF0 :: 0x00000000 // NOTE: not in headers
FF1 :: 0x00004000 // NOTE: not in headers
B0 :: 0
B50 :: 50
B75 :: 75
B110 :: 110
B134 :: 134
B150 :: 150
B200 :: 200
B300 :: 300
B600 :: 600
B1200 :: 1200
B1800 :: 1800
B2400 :: 2400
B4800 :: 4800
B9600 :: 9600
B19200 :: 19200
B38400 :: 38400
_CSIZE :: 0x00000300
CS5 :: 0x00000000
CS6 :: 0x00000100
CS7 :: 0x00000200
CS8 :: 0x00000300
CSTOPB :: 0x00000400
CREAD :: 0x00000800
PARENB :: 0x00001000
PARODD :: 0x00002000
HUPCL :: 0x00004000
CLOCAL :: 0x00008000
ECHO :: 0x00000008
ECHOE :: 0x00000002
ECHOK :: 0x00000004
ECHONL :: 0x00000010
ICANON :: 0x00000100
IEXTEN :: 0x00000400
ISIG :: 0x00000080
NOFLSH :: 0x80000000
TOSTOP :: 0x00400000
TCIFLUSH :: 1
TCOFLUSH :: 2
TCIOFLUSH :: 3
TCIOFF :: 3
TCION :: 4
TCOOFF :: 1
TCOON :: 2
} else {
#panic("posix is unimplemented for the current target")
}

234
core/sys/posix/time.odin Normal file
View File

@@ -0,0 +1,234 @@
package posix
import "core:c"
import "core:c/libc"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// time.h - time types
foreign lib {
/*
Convert the broken down time in the structure to a string form: Sun Sep 16 01:03:52 1973\n\0
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/asctime_r.html ]]
*/
asctime_r :: proc(tm: ^tm, buf: [^]c.char) -> cstring ---
/*
Convert a time value to a date and time string in the same format as asctime().
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/ctime_r.html ]]
*/
@(link_name=LCTIMER)
ctime_r :: proc(clock: ^time_t, buf: [^]c.char) -> cstring ---
/*
Converts the time in seconds since epoch to a broken-down tm struct.
Returns: nil (setting errno) on failure, the result pointer on success.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/gmtime_r.html ]]
*/
@(link_name=LGMTIMER)
gmtime_r :: proc(timer: ^time_t, result: ^tm) -> ^tm ---
/*
Convert the time in seconds since epoch to a broken-down tm struct in local time.
Returns: nil (setting errno) on failure, the result pointer on success.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/localtime_r.html ]]
*/
@(link_name=LLOCALTIMER)
localtime_r :: proc(timer: ^time_t, result: ^tm) -> ^tm ---
/*
Returns the resolution of any clock.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/clock_getres.html ]]
*/
@(link_name=LCLOCKGETRES)
clock_getres :: proc(clock_id: Clock, res: ^timespec) -> result ---
/*
Returns the current value tp for the specified clock, clock_id.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/clock_getres.html ]]
*/
@(link_name=LCLOCKGETTIME)
clock_gettime :: proc(clock_id: Clock, tp: ^timespec) -> result ---
/*
Sets the specified clock's time.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/clock_getres.html ]]
*/
@(link_name=LCLOCKSETTIME)
clock_settime :: proc(clock_id: Clock, tp: ^timespec) -> result ---
/*
Converts a string representation of a date or time into a broken-down time.
Returns: nil (setting getdate_err) on failure, the broken-down time otherwise
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getdate.html ]]
*/
getdate :: proc(string: cstring) -> ^tm ---
/*
Causes the current thread to be suspended from execution until either the time interval
specified by rqtp has elapsed or a signal is delivered.
Returns: -1 on failure (setting errno), if it was due to a signal, rmtp will be filled with the
remaining time, 0 if all time has been slept
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/nanosleep.html ]]
*/
@(link_name=LNANOSLEEP)
nanosleep :: proc(rqtp: ^timespec, rmtp: ^timespec) -> result ---
/*
Converts the character string to values which are stored in tm, using the specified format.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/strptime.html ]]
*/
strptime :: proc(buf: [^]c.char, format: cstring, tm: ^tm) -> cstring ---
/*
Uses the value of the environment variable TZ (or default) to set time conversion info.
`daylight` is set to whether daylight saving time conversion should be done.
`timezone` is set to the difference, in seconds, between UTC and local standard time.
`tzname` is set by `tzname[0] = "std"` and `tzname[1] = "dst"`
Example:
posix.tzset()
fmt.println(posix.tzname)
fmt.println(posix.daylight)
fmt.println(posix.timezone)
Possible Output:
["CET", "CEST"]
true
-3600
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/tzset.html ]]
*/
tzset :: proc() ---
// Whether daylight saving conversion should be done.
daylight: b32
// The time in seconds between UTC and local standard time.
@(link_name=LTIMEZONE)
timezone: c.long
tzname: [2]cstring
}
time_t :: libc.time_t
clock_t :: libc.clock_t
tm :: libc.tm
timespec :: libc.timespec
CLOCKS_PER_SEC :: libc.CLOCKS_PER_SEC
asctime :: libc.asctime
clock :: libc.clock
ctime :: libc.ctime
difftime :: libc.difftime
gmtime :: libc.gmtime
localtime :: libc.localtime
mktime :: libc.mktime
strftime :: libc.strftime
time :: libc.time
Clock :: enum clockid_t {
// system-wide monotonic clock, defined as clock measuring real time,
// can be set with clock_settime() and cannot have negative clock jumps.
MONOTONIC = CLOCK_MONOTONIC,
// CPU-time clock associated with the process making a clock() function call.
PROCESS_CPUTIME_ID = CLOCK_PROCESS_CPUTIME_ID,
// system-wide clock measuring real time.
REALTIME = CLOCK_REALTIME,
// CPU-time clock associated with the thread making a clock() function call.
THREAD_CPUTIME_ID = CLOCK_THREAD_CPUTIME_ID,
}
when ODIN_OS == .NetBSD {
@(private) LCTIMER :: "__ctime_r50"
@(private) LGMTIMER :: "__gmtime_r50"
@(private) LLOCALTIMER :: "__localtime_r50"
@(private) LCLOCKGETRES :: "__clock_getres50"
@(private) LCLOCKGETTIME :: "__clock_gettime50"
@(private) LCLOCKSETTIME :: "__clock_settime50"
@(private) LNANOSLEEP :: "__nanosleep50"
@(private) LTIMEZONE :: "__timezone13"
} else {
@(private) LCTIMER :: "ctime_r"
@(private) LGMTIMER :: "gmtime_r"
@(private) LLOCALTIMER :: "localtime_r"
@(private) LCLOCKGETRES :: "clock_getres"
@(private) LCLOCKGETTIME :: "clock_gettime"
@(private) LCLOCKSETTIME :: "clock_settime"
@(private) LNANOSLEEP :: "nanosleep"
@(private) LTIMEZONE :: "timezone"
}
when ODIN_OS == .Darwin {
clockid_t :: distinct c.int
CLOCK_MONOTONIC :: 6
CLOCK_PROCESS_CPUTIME_ID :: 12
CLOCK_REALTIME :: 0
CLOCK_THREAD_CPUTIME_ID :: 16
foreign lib {
getdate_err: Errno
}
} else when ODIN_OS == .FreeBSD {
clockid_t :: distinct c.int
CLOCK_MONOTONIC :: 4
CLOCK_PROCESS_CPUTIME_ID :: 15
CLOCK_REALTIME :: 0
CLOCK_THREAD_CPUTIME_ID :: 14
foreign lib {
getdate_err: Errno
}
} else when ODIN_OS == .NetBSD {
clockid_t :: distinct c.uint
CLOCK_MONOTONIC :: 3
CLOCK_PROCESS_CPUTIME_ID :: 0x40000000
CLOCK_REALTIME :: 0
CLOCK_THREAD_CPUTIME_ID :: 0x20000000
foreign lib {
getdate_err: Errno
}
} else when ODIN_OS == .OpenBSD {
clockid_t :: distinct c.uint
CLOCK_MONOTONIC :: 3
CLOCK_PROCESS_CPUTIME_ID :: 2
CLOCK_REALTIME :: 0
CLOCK_THREAD_CPUTIME_ID :: 4
getdate_err: Errno = .ENOSYS // NOTE: looks like it's not a thing on OpenBSD.
} else {
#panic("posix is unimplemented for the current target")
}

View File

@@ -0,0 +1,43 @@
package posix
import "core:c"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// ulimit.h - ulimit commands
foreign lib {
/*
Control process limits.
Note that -1 is a valid return value, applications should clear errno, do this call and then
check both -1 and the errno to determine status.
Returns: -1 (setting errno) on failure.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/ulimit.html ]]
*/
ulimit :: proc(i: c.int, #c_vararg arg: ..c.long) -> c.long ---
}
Ulimit_Cmd :: enum c.int {
// Returns the file size limit of the process in units of 512-byte blocks inherited by children.
GETFSIZE = UL_GETFSIZE,
// Set the file size limit for output operations, taken as a long, multiplied by 512.
SETFSIZE = UL_SETFSIZE,
}
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
UL_GETFSIZE :: 1
UL_SETFSIZE :: 2
// NOTE: I don't think OpenBSD implements this API.
} else {
#panic("posix is unimplemented for the current target")
}

1917
core/sys/posix/unistd.odin Normal file

File diff suppressed because it is too large Load Diff

36
core/sys/posix/utime.odin Normal file
View File

@@ -0,0 +1,36 @@
package posix
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// utime.h - access and modification time structure
foreign lib {
/*
Set file access and modification times.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/utime.html ]]
*/
@(link_name=LUTIME)
utime :: proc(path: cstring, times: ^utimbuf) -> result ---
}
when ODIN_OS == .NetBSD {
@(private) LUTIME :: "__utime50"
} else {
@(private) LUTIME :: "utime"
}
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
utimbuf :: struct {
actime: time_t, /* [PSX] access time (seconds since epoch) */
modtime: time_t, /* [PSX] modification time (seconds since epoch) */
}
} else {
#panic("posix is unimplemented for the current target")
}

107
core/sys/posix/wordexp.odin Normal file
View File

@@ -0,0 +1,107 @@
package posix
import "core:c"
when ODIN_OS == .Darwin {
foreign import lib "system:System.framework"
} else {
foreign import lib "system:c"
}
// wordexp.h - word-expansion type
foreign lib {
/*
Perform word expansion.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/wordexp.html ]]
*/
wordexp :: proc(words: cstring, pwordexp: ^wordexp_t, flags: WRDE_Flags) -> WRDE_Errno ---
/*
Free the space allocated during word expansion.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/wordexp.html ]]
*/
wordfree :: proc(pwordexp: ^wordexp_t) ---
}
WRDE_Flag_Bits :: enum c.int {
// Appends words to those previously generated.
APPEND = log2(WRDE_APPEND),
// Number of null pointers to prepend to we_wordv.
DOOFFS = log2(WRDE_DOOFFS),
// Fail if command substitution is requested.
NOCMD = log2(WRDE_NOCMD),
// The pwordexp argument was passed to a previous successful call to wordexp(),
// and has not been passed to wordfree().
REUSE = log2(WRDE_REUSE),
// Do not redirect stderr to /dev/null.
SHOWERR = log2(WRDE_SHOWERR),
// Report error on attempt to expand an undefined shell variable.
UNDEF = log2(WRDE_UNDEF),
}
WRDE_Flags :: bit_set[WRDE_Flag_Bits; c.int]
WRDE_Errno :: enum c.int {
OK = 0,
// One of the unquoted characters- <newline>, '|', '&', ';', '<', '>', '(', ')', '{', '}' -
// appears in words in an inappropriate context.
BADCHAR = WRDE_BADCHAR,
// Reference to undefined shell variable when WRDE_UNDEF is set in flags.
BADVAL = WRDE_BADVAL,
// Command substitution requested when WRDE_NOCMD was set in flags.
CMDSUB = WRDE_CMDSUB,
// Attempt to allocate memory failed.
NOSPACE = WRDE_NOSPACE,
// Shell syntax error, such as unbalanced parentheses or an unterminated string.
SYNTAX = WRDE_SYNTAX,
}
when ODIN_OS == .Darwin {
wordexp_t :: struct {
we_wordc: c.size_t, /* [PSX] count of words matched by words */
we_wordv: [^]cstring, /* [PSX] pointer to list of expanded words */
we_offs: c.size_t, /* [PSX] slots to reserve at the beginning of we_wordv */
}
WRDE_APPEND :: 0x01
WRDE_DOOFFS :: 0x02
WRDE_NOCMD :: 0x04
WRDE_REUSE :: 0x08
WRDE_SHOWERR :: 0x10
WRDE_UNDEF :: 0x20
WRDE_BADCHAR :: 1
WRDE_BADVAL :: 2
WRDE_CMDSUB :: 3
WRDE_NOSPACE :: 4
WRDE_SYNTAX :: 6
} else when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
wordexp_t :: struct {
we_wordc: c.size_t, /* [PSX] count of words matched by words */
we_wordv: [^]cstring, /* [PSX] pointer to list of expanded words */
we_offs: c.size_t, /* [PSX] slots to reserve at the beginning of we_wordv */
we_strings: [^]byte, /* storage for wordv strings */
we_nbytes: c.size_t, /* size of we_strings */
}
WRDE_APPEND :: 0x01
WRDE_DOOFFS :: 0x02
WRDE_NOCMD :: 0x04
WRDE_REUSE :: 0x08
WRDE_SHOWERR :: 0x10
WRDE_UNDEF :: 0x20
WRDE_BADCHAR :: 1
WRDE_BADVAL :: 2
WRDE_CMDSUB :: 3
WRDE_NOSPACE :: 4
WRDE_SYNTAX :: 6
} else {
#panic("posix is unimplemented for the current target")
}

View File

@@ -2,7 +2,7 @@ package unix
import "core:c"
pthread_t :: distinct u64
pthread_t :: distinct rawptr
SEM_T_SIZE :: 8

View File

@@ -5,6 +5,11 @@ foreign import "system:pthread"
import "core:c"
timespec :: struct {
tv_sec: i64,
tv_nsec: i64,
}
//
// On success, these functions return 0.
//

View File

@@ -67,6 +67,8 @@ CTL_KERN :: 1
KERN_VERSION :: 4 // Darwin Kernel Version 21.5.0: Tue Apr 26 21:08:22 PDT 2022; root:darwin-8020.121.3~4/RELEASE_X86_64
KERN_OSRELDATE :: 26 // i32: OS release date
KERN_OSVERSION :: 65 // Build number, e.g. 21F79
KERN_PROCARGS :: 38
KERN_PROCARGS2 :: 49
CTL_VM :: 2
CTL_VFS :: 3
CTL_NET :: 4
@@ -82,4 +84,4 @@ CTL_HW :: 6
HW_AVAILCPU :: 25 /* int: number of available CPUs */
CTL_MACHDEP :: 7
CTL_USER :: 8
CTL_USER :: 8

View File

@@ -23,6 +23,8 @@ CTL_KERN :: 1
KERN_OSRELEASE :: 2
KERN_OSREV :: 3
KERN_VERSION :: 4
KERN_PROC :: 14
KERN_PROC_PATHNAME :: 12
CTL_VM :: 2
CTL_VFS :: 3
CTL_NET :: 4

Some files were not shown because too many files have changed in this diff Show More