mirror of
https://github.com/odin-lang/Odin.git
synced 2026-02-16 08:04:07 +00:00
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:
@@ -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 ---
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
//+private
|
||||
//+build !darwin
|
||||
//+build !freebsd
|
||||
//+build !openbsd
|
||||
//+build !netbsd
|
||||
//+build !linux
|
||||
//+build !windows
|
||||
package mem_virtual
|
||||
|
||||
69
core/mem/virtual/virtual_posix.odin
Normal file
69
core/mem/virtual/virtual_posix.odin
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
92
core/os/os2/dir_posix.odin
Normal file
92
core/os/os2/dir_posix.odin
Normal 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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
77
core/os/os2/env_posix.odin
Normal file
77
core/os/os2/env_posix.odin
Normal 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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)))
|
||||
|
||||
30
core/os/os2/errors_posix.odin
Normal file
30
core/os/os2/errors_posix.odin
Normal 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)
|
||||
}
|
||||
}
|
||||
@@ -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
454
core/os/os2/file_posix.odin
Normal 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, ×) != .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), ×) != .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
|
||||
}
|
||||
}
|
||||
18
core/os/os2/file_posix_darwin.odin
Normal file
18
core/os/os2/file_posix_darwin.odin
Normal 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)
|
||||
}
|
||||
47
core/os/os2/file_posix_freebsd.odin
Normal file
47
core/os/os2/file_posix_freebsd.odin
Normal 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)
|
||||
}
|
||||
18
core/os/os2/file_posix_netbsd.odin
Normal file
18
core/os/os2/file_posix_netbsd.odin
Normal 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)
|
||||
}
|
||||
21
core/os/os2/file_posix_other.odin
Normal file
21
core/os/os2/file_posix_other.odin
Normal 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)
|
||||
}
|
||||
@@ -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])
|
||||
|
||||
7
core/os/os2/heap_posix.odin
Normal file
7
core/os/os2/heap_posix.odin
Normal file
@@ -0,0 +1,7 @@
|
||||
//+private
|
||||
//+build darwin, netbsd, freebsd, openbsd
|
||||
package os2
|
||||
|
||||
import "base:runtime"
|
||||
|
||||
_heap_allocator_proc :: runtime.heap_allocator_proc
|
||||
@@ -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)
|
||||
|
||||
@@ -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
126
core/os/os2/path_posix.odin
Normal 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
|
||||
}
|
||||
37
core/os/os2/pipe_posix.odin
Normal file
37
core/os/os2/pipe_posix.odin
Normal 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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
69
core/os/os2/process_posix.odin
Normal file
69
core/os/os2/process_posix.odin
Normal 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
|
||||
}
|
||||
255
core/os/os2/process_posix_darwin.odin
Normal file
255
core/os/os2/process_posix_darwin.odin
Normal 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
|
||||
}
|
||||
15
core/os/os2/process_posix_other.odin
Normal file
15
core/os/os2/process_posix_other.odin
Normal 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
|
||||
}
|
||||
@@ -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
137
core/os/os2/stat_posix.odin
Normal 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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
20
core/os/os2/temp_file_posix.odin
Normal file
20
core/os/os2/temp_file_posix.odin
Normal 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)
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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) ---
|
||||
|
||||
}
|
||||
|
||||
@@ -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
168
core/sys/darwin/proc.odin
Normal 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
|
||||
58
core/sys/posix/arpa_inet.odin
Normal file
58
core/sys/posix/arpa_inet.odin
Normal 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
205
core/sys/posix/dirent.odin
Normal 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
117
core/sys/posix/dlfcn.odin
Normal 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
373
core/sys/posix/errno.odin
Normal 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
415
core/sys/posix/fcntl.odin
Normal 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")
|
||||
}
|
||||
58
core/sys/posix/fnmatch.odin
Normal file
58
core/sys/posix/fnmatch.odin
Normal 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
179
core/sys/posix/glob.odin
Normal 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
130
core/sys/posix/grp.odin
Normal 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
50
core/sys/posix/iconv.odin
Normal 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 ---
|
||||
}
|
||||
285
core/sys/posix/langinfo.odin
Normal file
285
core/sys/posix/langinfo.odin
Normal 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")
|
||||
}
|
||||
74
core/sys/posix/libgen.odin
Normal file
74
core/sys/posix/libgen.odin
Normal 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
459
core/sys/posix/limits.odin
Normal 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")
|
||||
}
|
||||
93
core/sys/posix/locale.odin
Normal file
93
core/sys/posix/locale.odin
Normal 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")
|
||||
}
|
||||
42
core/sys/posix/monetary.odin
Normal file
42
core/sys/posix/monetary.odin
Normal 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 ---
|
||||
}
|
||||
60
core/sys/posix/net_if.odin
Normal file
60
core/sys/posix/net_if.odin
Normal 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
443
core/sys/posix/netdb.odin
Normal 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")
|
||||
}
|
||||
199
core/sys/posix/netinet_in.odin
Normal file
199
core/sys/posix/netinet_in.odin
Normal 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")
|
||||
}
|
||||
11
core/sys/posix/netinet_tcp.odin
Normal file
11
core/sys/posix/netinet_tcp.odin
Normal 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
78
core/sys/posix/poll.odin
Normal 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
70
core/sys/posix/posix.odin
Normal 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
518
core/sys/posix/pthread.odin
Normal 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
168
core/sys/posix/pwd.odin
Normal 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
105
core/sys/posix/sched.odin
Normal 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")
|
||||
}
|
||||
58
core/sys/posix/setjmp.odin
Normal file
58
core/sys/posix/setjmp.odin
Normal 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
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
332
core/sys/posix/stdio.odin
Normal 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
450
core/sys/posix/stdlib.odin
Normal 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"
|
||||
}
|
||||
47
core/sys/posix/string.odin
Normal file
47
core/sys/posix/string.odin
Normal 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())
|
||||
}
|
||||
89
core/sys/posix/sys_ipc.odin
Normal file
89
core/sys/posix/sys_ipc.odin
Normal 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")
|
||||
}
|
||||
229
core/sys/posix/sys_mman.odin
Normal file
229
core/sys/posix/sys_mman.odin
Normal 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
161
core/sys/posix/sys_msg.odin
Normal 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")
|
||||
}
|
||||
152
core/sys/posix/sys_resource.odin
Normal file
152
core/sys/posix/sys_resource.odin
Normal 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")
|
||||
}
|
||||
120
core/sys/posix/sys_select.odin
Normal file
120
core/sys/posix/sys_select.odin
Normal 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
132
core/sys/posix/sys_sem.odin
Normal 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
146
core/sys/posix/sys_shm.odin
Normal 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")
|
||||
}
|
||||
495
core/sys/posix/sys_socket.odin
Normal file
495
core/sys/posix/sys_socket.odin
Normal 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")
|
||||
}
|
||||
|
||||
432
core/sys/posix/sys_stat.odin
Normal file
432
core/sys/posix/sys_stat.odin
Normal 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")
|
||||
}
|
||||
135
core/sys/posix/sys_statvfs.odin
Normal file
135
core/sys/posix/sys_statvfs.odin
Normal 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")
|
||||
}
|
||||
82
core/sys/posix/sys_time.odin
Normal file
82
core/sys/posix/sys_time.odin
Normal 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")
|
||||
}
|
||||
38
core/sys/posix/sys_times.odin
Normal file
38
core/sys/posix/sys_times.odin
Normal 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")
|
||||
}
|
||||
42
core/sys/posix/sys_uio.odin
Normal file
42
core/sys/posix/sys_uio.odin
Normal 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")
|
||||
}
|
||||
17
core/sys/posix/sys_un.odin
Normal file
17
core/sys/posix/sys_un.odin
Normal 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")
|
||||
}
|
||||
55
core/sys/posix/sys_utsname.odin
Normal file
55
core/sys/posix/sys_utsname.odin
Normal 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")
|
||||
}
|
||||
380
core/sys/posix/sys_wait.odin
Normal file
380
core/sys/posix/sys_wait.odin
Normal 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
475
core/sys/posix/termios.odin
Normal 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
234
core/sys/posix/time.odin
Normal 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")
|
||||
}
|
||||
43
core/sys/posix/ulimit.odin
Normal file
43
core/sys/posix/ulimit.odin
Normal 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
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
36
core/sys/posix/utime.odin
Normal 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
107
core/sys/posix/wordexp.odin
Normal 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")
|
||||
}
|
||||
@@ -2,7 +2,7 @@ package unix
|
||||
|
||||
import "core:c"
|
||||
|
||||
pthread_t :: distinct u64
|
||||
pthread_t :: distinct rawptr
|
||||
|
||||
SEM_T_SIZE :: 8
|
||||
|
||||
|
||||
@@ -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.
|
||||
//
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user