Bladeren bron

posix: add package

Laytan Laats 1 jaar geleden
bovenliggende
commit
efe68c2e24
83 gewijzigde bestanden met toevoegingen van 12666 en 329 verwijderingen
  1. 2 2
      core/c/libc/complex.odin
  2. 1 1
      core/c/libc/errno.odin
  3. 12 7
      core/c/libc/setjmp.odin
  4. 35 8
      core/c/libc/stdio.odin
  5. 17 4
      core/c/libc/stdlib.odin
  6. 1 1
      core/c/libc/string.odin
  7. 29 3
      core/c/libc/time.odin
  8. 26 127
      core/mem/virtual/virtual_darwin.odin
  9. 1 0
      core/os/dir_unix.odin
  10. 35 37
      core/os/os_darwin.odin
  11. 5 5
      core/os/os_freebsd.odin
  12. 3 3
      core/os/os_linux.odin
  13. 4 4
      core/os/os_netbsd.odin
  14. 3 3
      core/os/os_openbsd.odin
  15. 3 4
      core/path/filepath/path_unix.odin
  16. 12 21
      core/prof/spall/spall_unix.odin
  17. 57 0
      core/sys/posix/README.md
  18. 58 0
      core/sys/posix/arpa_inet.odin
  19. 205 0
      core/sys/posix/dirent.odin
  20. 115 0
      core/sys/posix/dlfcn.odin
  21. 373 0
      core/sys/posix/errno.odin
  22. 415 0
      core/sys/posix/fcntl.odin
  23. 58 0
      core/sys/posix/fnmatch.odin
  24. 179 0
      core/sys/posix/glob.odin
  25. 130 0
      core/sys/posix/grp.odin
  26. 50 0
      core/sys/posix/iconv.odin
  27. 285 0
      core/sys/posix/langinfo.odin
  28. 74 0
      core/sys/posix/libgen.odin
  29. 459 0
      core/sys/posix/limits.odin
  30. 93 0
      core/sys/posix/locale.odin
  31. 42 0
      core/sys/posix/monetary.odin
  32. 60 0
      core/sys/posix/net_if.odin
  33. 443 0
      core/sys/posix/netdb.odin
  34. 199 0
      core/sys/posix/netinet_in.odin
  35. 11 0
      core/sys/posix/netinet_tcp.odin
  36. 78 0
      core/sys/posix/poll.odin
  37. 26 0
      core/sys/posix/posix.odin
  38. 518 0
      core/sys/posix/pthread.odin
  39. 168 0
      core/sys/posix/pwd.odin
  40. 105 0
      core/sys/posix/sched.odin
  41. 58 0
      core/sys/posix/setjmp.odin
  42. 1131 0
      core/sys/posix/signal.odin
  43. 332 0
      core/sys/posix/stdio.odin
  44. 450 0
      core/sys/posix/stdlib.odin
  45. 47 0
      core/sys/posix/string.odin
  46. 89 0
      core/sys/posix/sys_ipc.odin
  47. 229 0
      core/sys/posix/sys_mman.odin
  48. 161 0
      core/sys/posix/sys_msg.odin
  49. 152 0
      core/sys/posix/sys_resource.odin
  50. 120 0
      core/sys/posix/sys_select.odin
  51. 132 0
      core/sys/posix/sys_sem.odin
  52. 146 0
      core/sys/posix/sys_shm.odin
  53. 491 0
      core/sys/posix/sys_socket.odin
  54. 540 0
      core/sys/posix/sys_stat.odin
  55. 135 0
      core/sys/posix/sys_statvfs.odin
  56. 82 0
      core/sys/posix/sys_time.odin
  57. 38 0
      core/sys/posix/sys_times.odin
  58. 42 0
      core/sys/posix/sys_uio.odin
  59. 17 0
      core/sys/posix/sys_un.odin
  60. 55 0
      core/sys/posix/sys_utsname.odin
  61. 380 0
      core/sys/posix/sys_wait.odin
  62. 475 0
      core/sys/posix/termios.odin
  63. 234 0
      core/sys/posix/time.odin
  64. 43 0
      core/sys/posix/ulimit.odin
  65. 1914 0
      core/sys/posix/unistd.odin
  66. 36 0
      core/sys/posix/utime.odin
  67. 107 0
      core/sys/posix/wordexp.odin
  68. 1 1
      core/sys/unix/pthread_netbsd.odin
  69. 5 0
      core/sys/unix/pthread_unix.odin
  70. 0 83
      core/sys/unix/time_unix.odin
  71. 38 0
      core/time/time_linux.odin
  72. 29 13
      core/time/time_unix.odin
  73. 6 0
      examples/all/all_posix.odin
  74. 4 0
      src/big_int.cpp
  75. 17 0
      src/check_builtin.cpp
  76. 18 2
      src/check_decl.cpp
  77. 4 0
      src/checker_builtin_procs.hpp
  78. 1 0
      tests/core/normal.odin
  79. 211 0
      tests/core/sys/posix/posix.odin
  80. 127 0
      tests/core/sys/posix/structs.odin
  81. 2 0
      tests/core/sys/posix/structs/.gitignore
  82. 103 0
      tests/core/sys/posix/structs/structs.c
  83. 74 0
      tests/core/sys/posix/structs/structs.odin

+ 2 - 2
core/c/libc/complex.odin

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

+ 1 - 1
core/c/libc/errno.odin

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

+ 12 - 7
core/c/libc/setjmp.odin

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

+ 35 - 8
core/c/libc/stdio.odin

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

+ 17 - 4
core/c/libc/stdlib.odin

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

+ 1 - 1
core/c/libc/string.odin

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

+ 29 - 3
core/c/libc/time.odin

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

+ 26 - 127
core/mem/virtual/virtual_darwin.odin

@@ -2,141 +2,45 @@
 //+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 ---
-}
+import "core:sys/posix"
+
+MAP_ANONYMOUS :: 0x1000 /* allocated from memory, swap space */
 
+MADV_FREE     :: 5      /* pages unneeded, discard contents */
 
 _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 {
+	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 {
-	result := _mprotect(data, size, PROT_READ|PROT_WRITE)
-	if result != 0 {
+	if posix.mprotect(data, size, { .READ, .WRITE }) != .OK {
 		return .Out_Of_Memory
 	}
+
 	return nil
 }
+
 _decommit :: proc "contextless" (data: rawptr, size: uint) {
-	_mprotect(data, size, PROT_NONE)
-	_madvise(data, size, MADV_FREE)
+	posix.mprotect(data, size, {})
+	posix.posix_madvise(data, size, transmute(posix.MAdvice)i32(MADV_FREE))
 }
+
 _release :: proc "contextless" (data: rawptr, size: uint) {
-	_munmap(data, size)
+	posix.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
+	#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
 }
 
 
@@ -149,16 +53,11 @@ _platform_memory_init :: proc() {
 
 
 _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 {
+	#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

+ 1 - 0
core/os/dir_unix.odin

@@ -1,6 +1,7 @@
 //+build darwin, linux, netbsd, freebsd, openbsd
 package os
 
+import "base:runtime"
 import "core:strings"
 
 @(require_results)

+ 35 - 37
core/os/os_darwin.odin

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

+ 5 - 5
core/os/os_freebsd.odin

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

+ 3 - 3
core/os/os_linux.odin

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

+ 4 - 4
core/os/os_netbsd.odin

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

+ 3 - 3
core/os/os_openbsd.odin

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

+ 3 - 4
core/path/filepath/path_unix.odin

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

+ 12 - 21
core/prof/spall/spall_unix.odin

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

+ 57 - 0
core/sys/posix/README.md

@@ -0,0 +1,57 @@
+# POSIX
+
+defines bindings for most posix APIs.
+
+If a header is added, all of it must be implemented.
+
+Each platform must define the exact same symbols, different values are allowed, even structs with different non-standard fields.
+
+APIs part of extensions may be left out completely if one target doesn't implement it.
+
+APIs with a direct replacement in `core` might not be implemented.
+
+Macros are emulated with force inlined functions.
+
+Struct fields defined by the posix standard (and thus portable) are documented with `[PSX]`.
+
+
+ADD A TEST FOR SIGINFO, one thread signalling and retrieving the signal out of siginfo or something.
+ADD A TEST FOR wait.h
+ADD A TEST FOR pthread.
+ADDD A test for stat.h.
+ADD A TEST FOR setjmp.h.
+HAIKU.
+
+Unimplemented POSIX 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 | Never seen or heard of it
+- 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
+
+TODO:
+- time.h | Docs

+ 58 - 0
core/sys/posix/arpa_inet.odin

@@ -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 - 0
core/sys/posix/dirent.odin

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

+ 115 - 0
core/sys/posix/dlfcn.odin

@@ -0,0 +1,115 @@
+package posix
+
+import "core:c"
+
+when ODIN_OS == .Darwin {
+	foreign import lib "system:System.framework"
+} 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 - 0
core/sys/posix/errno.odin

@@ -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 - 0
core/sys/posix/fcntl.odin

@@ -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, arg: rawptr = nil) -> 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{}
+
+	O_EXEC    :: 0x00040000
+	O_RDONLY  :: 0
+	O_RDWR    :: 0x0002
+	O_WRONLY  :: 0x0001
+
+	_O_SEARCH :: O_EXEC|O_DIRECTORY
+	O_SEARCH  :: O_Flags{ .EXEC, .DIRECTORY }
+
+	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 - 0
core/sys/posix/fnmatch.odin

@@ -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 - 0
core/sys/posix/glob.odin

@@ -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 - 0
core/sys/posix/grp.odin

@@ -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 - 0
core/sys/posix/iconv.odin

@@ -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 - 0
core/sys/posix/langinfo.odin

@@ -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 - 0
core/sys/posix/libgen.odin

@@ -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 - 0
core/sys/posix/limits.odin

@@ -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 - 0
core/sys/posix/locale.odin

@@ -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 - 0
core/sys/posix/monetary.odin

@@ -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 - 0
core/sys/posix/net_if.odin

@@ -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 - 0
core/sys/posix/netdb.odin

@@ -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 - 0
core/sys/posix/netinet_in.odin

@@ -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 - 0
core/sys/posix/netinet_tcp.odin

@@ -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 - 0
core/sys/posix/poll.odin

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

+ 26 - 0
core/sys/posix/posix.odin

@@ -0,0 +1,26 @@
+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 - 0
core/sys/posix/pthread.odin

@@ -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 - 0
core/sys/posix/pwd.odin

@@ -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 - 0
core/sys/posix/sched.odin

@@ -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 - 0
core/sys/posix/setjmp.odin

@@ -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 - 0
core/sys/posix/signal.odin

@@ -0,0 +1,1131 @@
+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"
+}
+
+// signal.h - signals
+
+foreign lib {
+	// LIBC:
+
+	/*
+	Set a signal handler.
+
+	func can either be:
+	- `auto_cast posix.SIG_DFL` setting the default handler for that specific signal
+	- `auto_cast posix.SIG_IGN` causing the specific signal to be ignored
+	- a custom signal handler
+
+	Returns: SIG_ERR (setting errno), the last value of func on success
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/signal.html ]]
+	*/
+	signal :: proc(sig: Signal, func: proc "c" (Signal)) -> proc "c" (Signal) ---
+
+	/*
+	Raises a signal, calling its handler and then returning.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/raise.html ]]
+	*/
+	raise :: proc(sig: Signal) -> result ---
+
+	// POSIX:
+
+	/*
+	Raise a signal to the process/group specified by pid.
+
+	If sig is 0, this function can be used to check if the pid is just checked for validity.
+
+	If pid is -1, the signal is sent to all processes that the current process has permission to send.
+
+	If pid is negative (not -1), the signal is sent to all processes in the group identifier by the
+	absolute value.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/kill.html ]]
+	*/
+	kill :: proc(pid: pid_t, sig: Signal) -> result ---
+
+	/*
+	Shorthand for `kill(-pgrp, sig)` which will kill all processes in the given process group.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/killpg.html ]]
+	*/
+	killpg :: proc(pgrp: pid_t, sig: Signal) -> result ---
+
+	/*
+	Writes a language-dependent message to stderror.
+
+	Example:
+		posix.psignal(.SIGSEGV, "that didn't go well")
+
+	Possible Output:
+		that didn't go well: Segmentation fault
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/psignal.html ]]
+	*/
+	psignal :: proc(signum: Signal, message: cstring) ---
+
+	/*
+	Send a signal to a thread.
+	
+	As with kill, if sig is 0, only validation (of the pthread_t given) is done and no signal is sent.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_kill.html ]]
+	*/
+	pthread_kill :: proc(thread: pthread_t, sig: Signal) -> Errno ---
+
+	/*
+	Examine and change blocked signals.
+
+	Equivalent to sigprocmask(), without the restriction that the call be made in a single-threaded process.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_sigmask.html ]]
+	*/
+	pthread_sigmask :: proc(how: Sig, set: ^sigset_t, oset: ^sigset_t) -> Errno ---
+
+	/*
+	Examine and change blocked signals in a single-threaded process.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_sigmask.html ]]
+	*/
+	@(link_name=LSIGPROCMASK)
+	sigprocmask :: proc(how: Sig, set: ^sigset_t, oldset: ^sigset_t) -> result ---
+
+	/*
+	Examine and change a signal action.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigaction.html ]]
+	*/
+	@(link_name=LSIGACTION)
+	sigaction :: proc(sig: Signal, act: ^sigaction_t, oact: ^sigaction_t) -> result ---
+
+	@(link_name=LSIGADDSET)
+	sigaddset :: proc(set: ^sigset_t, signo: Signal) -> result ---
+	@(link_name=LSIGDELSET)
+	sigdelset :: proc(^sigset_t, Signal) -> c.int ---
+	@(link_name=LSIGEMPTYSET)
+	sigemptyset :: proc(^sigset_t) -> c.int ---
+	@(link_name=LSIGFILLSET)
+	sigfillset :: proc(^sigset_t) -> c.int ---
+
+	/*
+	Set and get the signal alternate stack context.
+
+	Example:
+		sigstk := posix.stack_t {
+			ss_sp    = make([^]byte, posix.SIGSTKSZ) or_else panic("allocation failure"),
+			ss_size  = posix.SIGSTKSZ,
+			ss_flags = {},
+		}
+		if posix.sigaltstack(&sigstk, nil) != .OK {
+			fmt.panicf("sigaltstack failure: %v", posix.strerror(posix.errno()))
+		}
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigaltstack.html ]]
+	*/
+	@(link_name=LSIGALTSTACK)
+	sigaltstack :: proc(ss: ^stack_t, oss: ^stack_t) -> result ---
+
+	/*
+	Adds sig to the signal mask of the calling process.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sighold.html ]]
+	*/
+	sighold :: proc(sig: Signal) -> result ---
+
+	/*
+	Sets the disposition of sig to SIG_IGN.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sighold.html ]]
+	*/
+	sigignore :: proc(sig: Signal) -> result ---
+
+	/*
+	Removes sig from the signal mask of the calling process and suspend the calling process until 
+	a signal is received.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sighold.html ]]
+	*/
+	sigpause :: proc(sig: Signal) -> result ---
+
+	/*
+	Removes sig from the signal mask of the calling process.
+
+	Returns: always -1.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sighold.html ]]
+	*/
+	sigrelse :: proc(sig: Signal) -> result ---
+
+	/*
+	Changes the restart behavior when a function is interrupted by the specified signal.
+
+	If flag is true, SA_RESTART is removed, added otherwise.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/siginterrupt.html ]]
+	*/
+	siginterrupt :: proc(sig: Signal, flag: b32) -> result ---
+
+	/*
+	Test for a signal in a signal set.
+
+	Returns: 1 if it is a member, 0 if not, -1 (setting errno) on failure
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigismember.html ]]
+	*/
+	@(link_name=LSIGISMEMBER)
+	sigismember :: proc(set: ^sigset_t, signo: Signal) -> c.int ---
+
+	/*
+	Stores the set of signals that are blocked from delivery to the calling thread and that are pending
+	on the process or the calling thread.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigpending.html ]]
+	*/
+	@(link_name=LSIGPENDING)
+	sigpending :: proc(set: ^sigset_t) -> result --- 
+
+	/*
+	Wait for one of the given signals.
+
+	Returns: always -1
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigsuspend.html ]]
+	*/
+	@(link_name=LSIGSUSPEND)
+	sigsuspend :: proc(sigmask: ^sigset_t) -> result ---
+
+	/*
+	Wait for queued signals.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigwait.html ]]
+	*/
+	sigwait :: proc(set: ^sigset_t, sig: ^Signal) -> Errno ---
+
+	/* NOTE: unimplemented on darwin.
+
+	void   psiginfo(const siginfo_t *, const char *);
+	int    sigqueue(pid_t, int, union sigval);
+	void (*sigset(int, void (*)(int)))(int);
+	int    sigsuspend(const sigset_t *);
+	int    sigtimedwait(const sigset_t *restrict, siginfo_t *restrict,
+	           const struct timespec *restrict);
+	int    sigwaitinfo(const sigset_t *restrict, siginfo_t *restrict);
+	*/
+}
+
+sigval :: struct #raw_union {
+	sigval_int: c.int,  /* [PSX] integer signal value */
+	sigval_ptr: rawptr, /* [PSX] pointer signal value */
+}
+
+Signal :: enum c.int {
+	NONE,
+
+	// LIBC:
+
+	// Process abort signal.
+	SIGABRT   = SIGABRT,
+	// Erronous arithemtic operation.
+	SIGFPE    = SIGFPE,
+	// Illegal instruction.
+	SIGILL    = SIGILL,
+	// Terminal interrupt signal.
+	SIGINT    = SIGINT,
+	// Invalid memory reference.
+	SIGSEGV   = SIGSEGV,
+	// Termination signal.
+	SIGTERM   = SIGTERM,
+
+	// POSIX:
+
+	// Process abort signal.
+	SIGALRM   = SIGALRM,
+	// Access to an undefined portion of a memory object.
+	SIGBUS    = SIGBUS,
+	// Child process terminated, stopped, or continued.
+	SIGCHLD   = SIGCHLD,
+	// Continue execution, if stopped.
+	SIGCONT   = SIGCONT,
+	// Hangup.
+	SIGHUP    = SIGHUP,
+	// Kill (cannot be caught or ignored).
+	SIGKILL   = SIGKILL,
+	// Write on a pipe with no one to read it.
+	SIGPIPE   = SIGPIPE,
+	// Terminal quit signal.
+	SIGQUIT   = SIGQUIT,
+	// Stop executing (cannot be caught or ignored).
+	SIGSTOP   = SIGSTOP,
+	// Terminal stop process.
+	SIGTSTP   = SIGTSTP,
+	// Background process attempting read.
+	SIGTTIN   = SIGTTIN,
+	// Background process attempting write.
+	SIGTTOU   = SIGTTOU,
+	// User-defined signal 1.
+	SIGUSR1   = SIGUSR1,
+	// User-defined signal 2.
+	SIGUSR2   = SIGUSR2,
+	// Pollable event.
+	SIGPOLL   = SIGPOLL,
+	// Profiling timer expired.
+	SIGPROF   = SIGPROF,
+	// Bad system call.
+	SIGSYS    = SIGSYS,
+	// Trace/breakpoint trap.
+	SIGTRAP   = SIGTRAP,
+	// High bandwidth data is available at a socket.
+	SIGURG    = SIGURG,
+	// Virtual timer expired.
+	SIGVTALRM = SIGVTALRM,
+	// CPU time limit exceeded.
+	SIGXCPU   = SIGXCPU,
+	// File size limit exceeded.
+	SIGXFSZ   = SIGXFSZ,
+}
+
+ILL_Code :: enum c.int {
+	// Illegal opcode.
+	ILLOPC = ILL_ILLOPC,
+	// Illegal operand.
+	ILLOPN = ILL_ILLOPN,
+	// Illegal addressing mode.
+	ILLADR = ILL_ILLADR,
+	// Illegal trap.
+	ILLTRP = ILL_ILLTRP,
+	// Priviledged opcode.
+	PRVOPC = ILL_PRVOPC,
+	// Priviledged register.
+	PRVREG = ILL_PRVREG,
+	// Coprocessor error.
+	COPROC = ILL_COPROC,
+	// Internal stack error.
+	BADSTK = ILL_BADSTK,
+}
+
+FPE_Code :: enum c.int {
+	// Integer divide by zero.
+	INTDIV = FPE_INTDIV,
+	// Integer overflow.
+	INTOVF = FPE_INTOVF,
+	// Floating-point divide by zero.
+	FLTDIV = FPE_FLTDIV,
+	// Floating-point overflow.
+	FLTOVF = FPE_FLTOVF,
+	// Floating-point underflow.
+	FLTUND = FPE_FLTUND,
+	// Floating-point inexact result.
+	FLTRES = FPE_FLTRES,
+	// Invalid floating-point operation.
+	FLTINV = FPE_FLTINV,
+	// Subscript out of range.
+	FLTSUB = FPE_FLTSUB,
+}
+
+SEGV_Code :: enum c.int {
+	// Address not mapped to object.
+	MAPERR = SEGV_MAPERR,
+	// Invalid permissions for mapped object.
+	ACCERR = SEGV_ACCERR,
+}
+
+BUS_Code :: enum c.int {
+	// Invalid address alignment.
+	ADRALN = BUS_ADRALN,
+	// Nonexistent physical address.
+	ADRERR = BUS_ADRERR,
+	// Object-specific hardware error.
+	OBJERR = BUS_OBJERR,
+}
+
+TRAP_Code :: enum c.int {
+	// Process breakpoint.
+	BRKPT = TRAP_BRKPT,
+	// Process trace trap.
+	TRACE = TRAP_TRACE,
+}
+
+CLD_Code :: enum c.int {
+	// Child has exited..
+	EXITED    = CLD_EXITED,
+	// Child has terminated abnormally and did not create a core file.
+	KILLED    = CLD_KILLED,
+	// Child has terminated abnormally and created a core file.
+	DUMPED    = CLD_DUMPED,
+	// Traced child trapped.
+	TRAPPED   = CLD_TRAPPED,
+	// Child has stopped.
+	STOPPED   = CLD_STOPPED,
+	// Stopped child has continued.
+	CONTINUED = CLD_CONTINUED,
+}
+
+POLL_Code :: enum c.int {
+	// Data input is available.
+	IN  = POLL_IN,
+	// Output buffers available.
+	OUT = POLL_OUT,
+	// Input message available.
+	MSG = POLL_MSG,
+	// I/O error.
+	ERR = POLL_ERR,
+	// High priority input available.
+	PRI = POLL_PRI,
+	// Device disconnected.
+	HUP = POLL_HUP,
+}
+
+Any_Code :: enum c.int {
+	// Signal sent by kill().
+	USER    = SI_USER,
+	// Signal sent by sigqueue().
+	QUEUE   = SI_QUEUE,
+	// Signal generated by expiration of a timer set by timer_settime().
+	TIMER   = SI_TIMER,
+	// Signal generated by completion of an asynchronous I/O request.
+	ASYNCIO = SI_ASYNCIO,
+	// Signal generated by arrival of a message on an empty message queue.
+	MESGQ   = SI_MESGQ,
+}
+
+SA_Flags_Bits :: enum c.int {
+	// Do not generate SIGCHLD when children stop or stopped children continue.
+	NOCLDSTOP  = log2(SA_NOCLDSTOP),
+	// Cause signal delivery to occur on an alternate stack.
+	ONSTACK    = log2(SA_ONSTACK),
+	// Cause signal disposition to be set to SIG_DFL on entry to signal handlers.
+	RESETHAND  = log2(SA_RESETHAND),
+	// Cause certain functions to become restartable.
+	RESTART    = log2(SA_RESTART),
+	// Cause extra information to be passed to signal handlers at the time of receipt of a signal.
+	SIGINFO    = log2(SA_SIGINFO),
+	// Cause implemention not to create zombie processes or status information on child termination.
+	NOCLDWAIT  = log2(SA_NOCLDWAIT),
+	// Cause signal not to be automatically blocked on entry to signal handler.
+	SA_NODEFER = log2(SA_NODEFER),
+}
+SA_Flags :: bit_set[SA_Flags_Bits; c.int]
+
+SS_Flag_Bits :: enum c.int {
+	// Process is executing on an alternate signal stack.
+	ONSTACK = log2(SS_ONSTACK),
+	// Alternate signal stack is disabled.
+	DISABLE = log2(SS_DISABLE),
+}
+SS_Flags :: bit_set[SS_Flag_Bits; c.int]
+
+Sig :: enum c.int {
+	// Resulting set is the union of the current set and the signal set and the complement of 
+	// the signal set pointed to by the argument.
+	BLOCK   = SIG_BLOCK,
+	// Resulting set is the intersection of the current set and the complement of the signal set
+	// pointed to by the argument.
+	UNBLOCK = SIG_UNBLOCK,
+	// Resulting set is the signal set pointed to by the argument.
+	SETMASK = SIG_SETMASK,
+}
+
+// Request for default signal handling.
+SIG_DFL :: libc.SIG_DFL
+// Return value from signal() in case of error.
+SIG_ERR :: libc.SIG_ERR
+// Request that signal be ignored.
+SIG_IGN :: libc.SIG_IGN
+
+SIGABRT :: libc.SIGABRT
+SIGFPE  :: libc.SIGFPE
+SIGILL  :: libc.SIGILL
+SIGINT  :: libc.SIGINT
+SIGSEGV :: libc.SIGSEGV
+SIGTERM :: libc.SIGTERM
+
+when ODIN_OS == .NetBSD {
+	@(private) LSIGPROCMASK :: "__sigprocmask14"
+	@(private) LSIGACTION   :: "__sigaction_siginfo"
+	@(private) LSIGADDSET   :: "__sigaddset14"
+	@(private) LSIGDELSET   :: "__sigdelset14"
+	@(private) LSIGEMPTYSET :: "__sigemptyset14"
+	@(private) LSIGFILLSET  :: "__sigfillset14"
+	@(private) LSIGALTSTACK :: "__sigaltstack14"
+	@(private) LSIGISMEMBER :: "__sigismember14"
+	@(private) LSIGPENDING  :: "__sigpending14"
+	@(private) LSIGSUSPEND  :: "__sigsuspend14"
+} else {
+	@(private) LSIGPROCMASK :: "sigprocmask"
+	@(private) LSIGACTION   :: "sigaction"
+	@(private) LSIGADDSET   :: "sigaddset"
+	@(private) LSIGDELSET   :: "sigdelset"
+	@(private) LSIGEMPTYSET :: "sigemptyset"
+	@(private) LSIGFILLSET  :: "sigfillset"
+	@(private) LSIGALTSTACK :: "sigaltstack"
+	@(private) LSIGISMEMBER :: "sigismember"
+	@(private) LSIGPENDING  :: "sigpending"
+	@(private) LSIGSUSPEND  :: "sigsuspend"
+}
+
+when ODIN_OS == .Darwin {
+
+	// Request that signal be held
+	SIG_HOLD :: rawptr(uintptr(5))
+
+	uid_t :: distinct c.uint32_t
+	sigset_t :: distinct c.uint32_t
+
+	// MOTE: unimplemented on darwin.
+	//
+	// SIGRTMIN :: 
+	// SIGRTMAX ::
+
+	SIGHUP    :: 1
+	SIGQUIT   :: 3
+	SIGTRAP   :: 5
+	SIGPOLL   :: 7
+	SIGKILL   :: 9
+	SIGBUS    :: 10
+	SIGSYS    :: 12
+	SIGPIPE   :: 13
+	SIGALRM   :: 14
+	SIGURG    :: 16
+	SIGCONT   :: 19
+	SIGSTOP   :: 17
+	SIGTSTP   :: 18
+	SIGCHLD   :: 20
+	SIGTTIN   :: 21
+	SIGTTOU   :: 22
+	SIGXCPU   :: 24
+	SIGXFSZ   :: 25
+	SIGVTALRM :: 26
+	SIGPROF   :: 27
+	SIGUSR1   :: 30
+	SIGUSR2   :: 31
+
+	// NOTE: this is actually defined as `sigaction`, but due to the function with the same name
+	// `_t` has been added.
+
+	sigaction_t :: struct {
+		using _: struct #raw_union {
+			sa_handler:   proc "c" (Signal),                     /* [PSX] signal-catching function or one of the SIG_IGN or SIG_DFL */
+			sa_sigaction: proc "c" (Signal, ^siginfo_t, rawptr), /* [PSX] signal-catching function */
+		},
+		sa_mask:  sigset_t, /* [PSX] set of signals to be blocked during execution of the signal handling function */
+		sa_flags: SA_Flags, /* [PSX] special flags */
+	}
+
+	SIG_BLOCK   :: 1
+	SIG_UNBLOCK :: 2
+	SIG_SETMASK :: 3
+
+	SA_NOCLDSTOP :: 0x0008
+	SA_ONSTACK   :: 0x0001
+	SA_RESETHAND :: 0x0004
+	SA_RESTART   :: 0x0002
+	SA_SIGINFO   :: 0x0040
+	SA_NOCLDWAIT :: 0x0020
+	SA_NODEFER   :: 0x0010
+
+	SS_ONSTACK :: 0x0001
+	SS_DISABLE :: 0x0004
+
+	MINSIGSTKSZ :: 32768
+	SIGSTKSZ    :: 131072
+
+	stack_t :: struct {
+		ss_sp:    rawptr,   /* [PSX] stack base or pointer */
+		ss_size:  c.size_t, /* [PSX] stack size */
+		ss_flags: SS_Flags, /* [PSX] flags */
+	}
+
+	siginfo_t :: struct {
+		si_signo:  Signal, /* [PSX] signal number */
+		si_errno:  Errno,  /* [PSX] errno value associated with this signal */
+		si_code: struct #raw_union { /* [PSX] specific more detailed codes per signal */
+			ill:  ILL_Code,
+			fpe:  FPE_Code,
+			segv: SEGV_Code,
+			bus:  BUS_Code,
+			trap: TRAP_Code,
+			chld: CLD_Code,
+			poll: POLL_Code,
+			any:  Any_Code,
+		},
+		si_pid:    pid_t,      /* [PSX] sending process ID */
+		si_uid:    uid_t,      /* [PSX] real user ID of sending process */
+		si_status: c.int,      /* [PSX] exit value of signal */
+		si_addr:   rawptr,     /* [PSX] address of faulting instruction */
+		si_value:  sigval,     /* [PSX] signal value */
+		si_band:   c.long,     /* [PSX] band event for SIGPOLL */
+		__pad:     [7]c.ulong,
+	}
+
+	ILL_ILLOPC :: 1
+	ILL_ILLOPN :: 4
+	ILL_ILLADR :: 5
+	ILL_ILLTRP :: 2
+	ILL_PRVOPC :: 3
+	ILL_PRVREG :: 6
+	ILL_COPROC :: 7
+	ILL_BADSTK :: 8
+
+	FPE_INTDIV :: 7
+	FPE_INTOVF :: 8
+	FPE_FLTDIV :: 1
+	FPE_FLTOVF :: 2
+	FPE_FLTUND :: 3
+	FPE_FLTRES :: 4
+	FPE_FLTINV :: 5
+	FPE_FLTSUB :: 6
+
+	SEGV_MAPERR :: 1
+	SEGV_ACCERR :: 2
+
+	BUS_ADRALN :: 1
+	BUS_ADRERR :: 2
+	BUS_OBJERR :: 3
+
+	TRAP_BRKPT :: 1
+	TRAP_TRACE :: 2
+
+	CLD_EXITED    :: 1
+	CLD_KILLED    :: 2
+	CLD_DUMPED    :: 3
+	CLD_TRAPPED   :: 4
+	CLD_STOPPED   :: 5
+	CLD_CONTINUED :: 6
+
+	POLL_IN  :: 1
+	POLL_OUT :: 2
+	POLL_MSG :: 3
+	POLL_ERR :: 4
+	POLL_PRI :: 5
+	POLL_HUP :: 6
+
+	SI_USER    :: 0x10001
+	SI_QUEUE   :: 0x10002
+	SI_TIMER   :: 0x10003
+	SI_ASYNCIO :: 0x10004
+	SI_MESGQ   :: 0x10005
+
+} else when ODIN_OS == .FreeBSD {
+
+	// Request that signal be held
+	SIG_HOLD :: rawptr(uintptr(3))
+
+	uid_t :: distinct c.uint32_t
+
+	sigset_t :: struct {
+		__bits: [4]c.uint32_t,
+	}
+
+	// MOTE: unimplemented on darwin.
+	//
+	// SIGRTMIN :: 65
+	// SIGRTMAX :: 126
+
+	SIGHUP    :: 1
+	SIGQUIT   :: 3
+	SIGTRAP   :: 5
+	SIGPOLL   :: 7
+	SIGKILL   :: 9
+	SIGBUS    :: 10
+	SIGSYS    :: 12
+	SIGPIPE   :: 13
+	SIGALRM   :: 14
+	SIGURG    :: 16
+	SIGCONT   :: 19
+	SIGSTOP   :: 17
+	SIGTSTP   :: 18
+	SIGCHLD   :: 20
+	SIGTTIN   :: 21
+	SIGTTOU   :: 22
+	SIGXCPU   :: 24
+	SIGXFSZ   :: 25
+	SIGVTALRM :: 26
+	SIGPROF   :: 27
+	SIGUSR1   :: 30
+	SIGUSR2   :: 31
+
+	// NOTE: this is actually defined as `sigaction`, but due to the function with the same name
+	// `_t` has been added.
+
+	sigaction_t :: struct {
+		using _: struct #raw_union {
+			sa_handler:   proc "c" (Signal),                     /* [PSX] signal-catching function or one of the SIG_IGN or SIG_DFL */
+			sa_sigaction: proc "c" (Signal, ^siginfo_t, rawptr), /* [PSX] signal-catching function */
+		},
+		sa_flags: SA_Flags, /* [PSX] special flags */
+		sa_mask:  sigset_t, /* [PSX] set of signals to be blocked during execution of the signal handling function */
+	}
+
+	SIG_BLOCK   :: 1
+	SIG_UNBLOCK :: 2
+	SIG_SETMASK :: 3
+
+	SA_NOCLDSTOP :: 0x0008
+	SA_ONSTACK   :: 0x0001
+	SA_RESETHAND :: 0x0004
+	SA_RESTART   :: 0x0002
+	SA_SIGINFO   :: 0x0040
+	SA_NOCLDWAIT :: 0x0020
+	SA_NODEFER   :: 0x0010
+
+	SS_ONSTACK :: 0x0001
+	SS_DISABLE :: 0x0004
+
+	when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm32 {
+		MINSIGSTKSZ :: 1024 * 4
+	} else when ODIN_ARCH == .amd64 || ODIN_ARCH == .i386 {
+		MINSIGSTKSZ :: 512 * 4
+	}
+
+	SIGSTKSZ :: MINSIGSTKSZ + 32768
+
+	stack_t :: struct {
+		ss_sp:    rawptr,   /* [PSX] stack base or pointer */
+		ss_size:  c.size_t, /* [PSX] stack size */
+		ss_flags: SS_Flags, /* [PSX] flags */
+	}
+
+	siginfo_t :: struct {
+		si_signo:  Signal, /* [PSX] signal number */
+		si_errno:  Errno,  /* [PSX] errno value associated with this signal */
+		si_code: struct #raw_union { /* [PSX] specific more detailed codes per signal */
+			ill:  ILL_Code,
+			fpe:  FPE_Code,
+			segv: SEGV_Code,
+			bus:  BUS_Code,
+			trap: TRAP_Code,
+			chld: CLD_Code,
+			poll: POLL_Code,
+			any:  Any_Code,
+		},
+		si_pid:    pid_t,      /* [PSX] sending process ID */
+		si_uid:    uid_t,      /* [PSX] real user ID of sending process */
+		si_status: c.int,      /* [PSX] exit value of signal */
+		si_addr:   rawptr,     /* [PSX] address of faulting instruction */
+		si_value:  sigval,     /* [PSX] signal value */
+		using _reason: struct #raw_union {
+			_fault: struct {
+				_trapno: c.int, /* machine specific trap code */
+			},
+			_timer: struct {
+				_timerid: c.int,
+				_overrun: c.int,
+			},
+			_mesgq: struct {
+				_mqd: c.int,
+			},
+			using _poll: struct {
+				si_band: c.long, /* [PSX] band event for SIGPOLL */
+			},
+			_capsicum: struct {
+				_syscall: c.int, /* syscall number for signals delivered as a result of system calls denied by capsicum */
+			},
+			__spare__: struct {
+				__spare1__: c.long,
+				__spare2__: [7]c.int,
+			},
+		},
+	}
+
+	ILL_ILLOPC :: 1
+	ILL_ILLOPN :: 2
+	ILL_ILLADR :: 3
+	ILL_ILLTRP :: 4
+	ILL_PRVOPC :: 5
+	ILL_PRVREG :: 6
+	ILL_COPROC :: 7
+	ILL_BADSTK :: 8
+
+	FPE_INTDIV :: 2
+	FPE_INTOVF :: 1
+	FPE_FLTDIV :: 3
+	FPE_FLTOVF :: 4
+	FPE_FLTUND :: 5
+	FPE_FLTRES :: 6
+	FPE_FLTINV :: 7
+	FPE_FLTSUB :: 8
+
+	SEGV_MAPERR :: 1
+	SEGV_ACCERR :: 2
+
+	BUS_ADRALN :: 1
+	BUS_ADRERR :: 2
+	BUS_OBJERR :: 3
+
+	TRAP_BRKPT :: 1
+	TRAP_TRACE :: 2
+
+	CLD_EXITED    :: 1
+	CLD_KILLED    :: 2
+	CLD_DUMPED    :: 3
+	CLD_TRAPPED   :: 4
+	CLD_STOPPED   :: 5
+	CLD_CONTINUED :: 6
+
+	POLL_IN  :: 1
+	POLL_OUT :: 2
+	POLL_MSG :: 3
+	POLL_ERR :: 4
+	POLL_PRI :: 5
+	POLL_HUP :: 6
+
+	SI_USER    :: 0x10001
+	SI_QUEUE   :: 0x10002
+	SI_TIMER   :: 0x10003
+	SI_ASYNCIO :: 0x10004
+	SI_MESGQ   :: 0x10005
+
+} else when ODIN_OS == .NetBSD {
+
+	// Request that signal be held
+	SIG_HOLD :: rawptr(uintptr(3))
+
+	uid_t :: distinct c.uint32_t
+	sigset_t :: struct {
+		__bits: [4]c.uint32_t,
+	}
+
+	// MOTE: unimplemented on darwin.
+	//
+	// SIGRTMIN :: 33
+	// SIGRTMAX :: 63
+
+	SIGHUP    :: 1
+	SIGQUIT   :: 3
+	SIGTRAP   :: 5
+	SIGPOLL   :: 7
+	SIGKILL   :: 9
+	SIGBUS    :: 10
+	SIGSYS    :: 12
+	SIGPIPE   :: 13
+	SIGALRM   :: 14
+	SIGURG    :: 16
+	SIGCONT   :: 19
+	SIGSTOP   :: 17
+	SIGTSTP   :: 18
+	SIGCHLD   :: 20
+	SIGTTIN   :: 21
+	SIGTTOU   :: 22
+	SIGXCPU   :: 24
+	SIGXFSZ   :: 25
+	SIGVTALRM :: 26
+	SIGPROF   :: 27
+	SIGUSR1   :: 30
+	SIGUSR2   :: 31
+
+	// NOTE: this is actually defined as `sigaction`, but due to the function with the same name
+	// `_t` has been added.
+
+	sigaction_t :: struct {
+		using _: struct #raw_union {
+			sa_handler:   proc "c" (Signal),                     /* [PSX] signal-catching function or one of the SIG_IGN or SIG_DFL */
+			sa_sigaction: proc "c" (Signal, ^siginfo_t, rawptr), /* [PSX] signal-catching function */
+		},
+		sa_mask:  sigset_t, /* [PSX] set of signals to be blocked during execution of the signal handling function */
+		sa_flags: SA_Flags, /* [PSX] special flags */
+	}
+
+	SIG_BLOCK   :: 1
+	SIG_UNBLOCK :: 2
+	SIG_SETMASK :: 3
+
+	SA_NOCLDSTOP :: 0x0008
+	SA_ONSTACK   :: 0x0001
+	SA_RESETHAND :: 0x0004
+	SA_RESTART   :: 0x0002
+	SA_SIGINFO   :: 0x0040
+	SA_NOCLDWAIT :: 0x0020
+	SA_NODEFER   :: 0x0010
+
+	SS_ONSTACK :: 0x0001
+	SS_DISABLE :: 0x0004
+
+	MINSIGSTKSZ :: 8192
+	SIGSTKSZ    :: MINSIGSTKSZ + 32768
+
+	stack_t :: struct {
+		ss_sp:    rawptr,   /* [PSX] stack base or pointer */
+		ss_size:  c.size_t, /* [PSX] stack size */
+		ss_flags: SS_Flags, /* [PSX] flags */
+	}
+
+	@(private)
+	lwpid_t :: c.int32_t
+
+	siginfo_t :: struct #raw_union {
+		si_pad: [128]byte,
+		using _info: struct {
+			si_signo: Signal, /* [PSX] signal number */
+			si_code: struct #raw_union { /* [PSX] specific more detailed codes per signal */
+				ill:  ILL_Code,
+				fpe:  FPE_Code,
+				segv: SEGV_Code,
+				bus:  BUS_Code,
+				trap: TRAP_Code,
+				chld: CLD_Code,
+				poll: POLL_Code,
+				any:  Any_Code,
+			},
+			si_errno: Errno,  /* [PSX] errno value associated with this signal */
+			// #ifdef _LP64
+			/* In _LP64 the union starts on an 8-byte boundary. */
+			_pad: c.int,
+			// #endif
+			using _reason: struct #raw_union {
+				using _rt: struct {
+					_pid:     pid_t,
+					_uid:     uid_t,
+					si_value: sigval,   /* [PSX] signal value */
+				},
+				using _child: struct {
+					si_pid:    pid_t,   /* [PSX] sending process ID */
+					si_uid:    uid_t,   /* [PSX] real user ID of sending process */
+					si_status: c.int,   /* [PSX] exit value of signal */
+					_utime:    clock_t,
+					_stime:    clock_t,
+				},
+				using _fault: struct {
+					si_addr: rawptr, /* [PSX] address of faulting instruction */
+					_trap:   c.int,
+					_trap2:  c.int,
+					_trap3:  c.int,
+				},
+				using _poll: struct {
+					si_band: c.long, /* [PSX] band event for SIGPOLL */
+					_fd:     FD,
+				},
+				_syscall: struct {
+					_sysnum:  c.int,
+					_retval: [2]c.int,
+					_error:   c.int,
+					_args:   [8]c.uint64_t,
+				},
+				_ptrace_state: struct {
+					_pe_report_event: c.int,
+					_option: struct #raw_union {
+						_pe_other_pid: pid_t,
+						_pe_lwp:       lwpid_t,
+					},
+				},
+			},
+		},
+	}
+
+	ILL_ILLOPC :: 1
+	ILL_ILLOPN :: 2
+	ILL_ILLADR :: 3
+	ILL_ILLTRP :: 4
+	ILL_PRVOPC :: 5
+	ILL_PRVREG :: 6
+	ILL_COPROC :: 7
+	ILL_BADSTK :: 8
+
+	FPE_INTDIV :: 1
+	FPE_INTOVF :: 2
+	FPE_FLTDIV :: 3
+	FPE_FLTOVF :: 4
+	FPE_FLTUND :: 5
+	FPE_FLTRES :: 6
+	FPE_FLTINV :: 7
+	FPE_FLTSUB :: 8
+
+	SEGV_MAPERR :: 1
+	SEGV_ACCERR :: 2
+
+	BUS_ADRALN :: 1
+	BUS_ADRERR :: 2
+	BUS_OBJERR :: 3
+
+	TRAP_BRKPT :: 1
+	TRAP_TRACE :: 2
+
+	CLD_EXITED    :: 1
+	CLD_KILLED    :: 2
+	CLD_DUMPED    :: 3
+	CLD_TRAPPED   :: 4
+	CLD_STOPPED   :: 5
+	CLD_CONTINUED :: 6
+
+	POLL_IN  :: 1
+	POLL_OUT :: 2
+	POLL_MSG :: 3
+	POLL_ERR :: 4
+	POLL_PRI :: 5
+	POLL_HUP :: 6
+
+	SI_USER    ::  0
+	SI_QUEUE   :: -1
+	SI_TIMER   :: -2
+	SI_ASYNCIO :: -3
+	SI_MESGQ   :: -4
+
+} else when ODIN_OS == .OpenBSD {
+
+	// Request that signal be held
+	SIG_HOLD :: rawptr(uintptr(3))
+
+	uid_t :: distinct c.uint32_t
+	sigset_t :: distinct c.uint32_t
+
+	SIGHUP    :: 1
+	SIGQUIT   :: 3
+	SIGTRAP   :: 5
+	SIGPOLL   :: 7
+	SIGKILL   :: 9
+	SIGBUS    :: 10
+	SIGSYS    :: 12
+	SIGPIPE   :: 13
+	SIGALRM   :: 14
+	SIGURG    :: 16
+	SIGCONT   :: 19
+	SIGSTOP   :: 17
+	SIGTSTP   :: 18
+	SIGCHLD   :: 20
+	SIGTTIN   :: 21
+	SIGTTOU   :: 22
+	SIGXCPU   :: 24
+	SIGXFSZ   :: 25
+	SIGVTALRM :: 26
+	SIGPROF   :: 27
+	SIGUSR1   :: 30
+	SIGUSR2   :: 31
+
+	// NOTE: this is actually defined as `sigaction`, but due to the function with the same name
+	// `_t` has been added.
+
+	sigaction_t :: struct {
+		using _: struct #raw_union {
+			sa_handler:   proc "c" (Signal),                     /* [PSX] signal-catching function or one of the SIG_IGN or SIG_DFL */
+			sa_sigaction: proc "c" (Signal, ^siginfo_t, rawptr), /* [PSX] signal-catching function */
+		},
+		sa_mask:  sigset_t, /* [PSX] set of signals to be blocked during execution of the signal handling function */
+		sa_flags: SA_Flags, /* [PSX] special flags */
+	}
+
+	SIG_BLOCK   :: 1
+	SIG_UNBLOCK :: 2
+	SIG_SETMASK :: 3
+
+	SA_NOCLDSTOP :: 0x0008
+	SA_ONSTACK   :: 0x0001
+	SA_RESETHAND :: 0x0004
+	SA_RESTART   :: 0x0002
+	SA_SIGINFO   :: 0x0040
+	SA_NOCLDWAIT :: 0x0020
+	SA_NODEFER   :: 0x0010
+
+	SS_ONSTACK :: 0x0001
+	SS_DISABLE :: 0x0004
+
+	MINSIGSTKSZ :: 3 << 12
+	SIGSTKSZ    :: MINSIGSTKSZ + (1 << 12) * 4
+
+	stack_t :: struct {
+		ss_sp:    rawptr,   /* [PSX] stack base or pointer */
+		ss_size:  c.size_t, /* [PSX] stack size */
+		ss_flags: SS_Flags, /* [PSX] flags */
+	}
+
+	SI_MAXSZ :: 128
+	SI_PAD   :: (SI_MAXSZ / size_of(c.int)) - 3
+
+	siginfo_t :: struct {
+		si_signo: Signal, /* [PSX] signal number */
+		si_code: struct #raw_union { /* [PSX] specific more detailed codes per signal */
+			ill:  ILL_Code,
+			fpe:  FPE_Code,
+			segv: SEGV_Code,
+			bus:  BUS_Code,
+			trap: TRAP_Code,
+			chld: CLD_Code,
+			poll: POLL_Code,
+			any:  Any_Code,
+		},
+		si_errno: Errno,  /* [PSX] errno value associated with this signal */
+		using _data: struct #raw_union {
+			_pad: [SI_PAD]c.int,
+			using _proc: struct {
+				si_pid: pid_t,   /* [PSX] sending process ID */
+				si_uid: uid_t,   /* [PSX] real user ID of sending process */
+				using _pdata: struct #raw_union {
+					using _kill: struct {
+						si_value: sigval,
+					},
+					using _cld: struct {
+						_utime:  clock_t,
+						_stime:  clock_t,
+						si_status: c.int,
+					},
+				},
+			},
+			using _fault: struct {
+				si_addr: rawptr,
+				_trapno: c.int,
+			},
+			using _file: struct {
+				_fd: FD,
+				si_band: c.long, /* [PSX] band event for SIGPOLL */
+			},
+		},
+	}
+
+	ILL_ILLOPC :: 1
+	ILL_ILLOPN :: 2
+	ILL_ILLADR :: 3
+	ILL_ILLTRP :: 4
+	ILL_PRVOPC :: 5
+	ILL_PRVREG :: 6
+	ILL_COPROC :: 7
+	ILL_BADSTK :: 8
+
+	FPE_INTDIV :: 1
+	FPE_INTOVF :: 2
+	FPE_FLTDIV :: 3
+	FPE_FLTOVF :: 4
+	FPE_FLTUND :: 5
+	FPE_FLTRES :: 6
+	FPE_FLTINV :: 7
+	FPE_FLTSUB :: 8
+
+	SEGV_MAPERR :: 1
+	SEGV_ACCERR :: 2
+
+	BUS_ADRALN :: 1
+	BUS_ADRERR :: 2
+	BUS_OBJERR :: 3
+
+	TRAP_BRKPT :: 1
+	TRAP_TRACE :: 2
+
+	CLD_EXITED    :: 1
+	CLD_KILLED    :: 2
+	CLD_DUMPED    :: 3
+	CLD_TRAPPED   :: 4
+	CLD_STOPPED   :: 5
+	CLD_CONTINUED :: 6
+
+	POLL_IN  :: 1
+	POLL_OUT :: 2
+	POLL_MSG :: 3
+	POLL_ERR :: 4
+	POLL_PRI :: 5
+	POLL_HUP :: 6
+
+	SI_USER    ::  0
+	SI_QUEUE   :: -2
+	SI_TIMER   :: -3
+	SI_ASYNCIO :: -4 // NOTE: not implemented
+	SI_MESGQ   :: -5 // NOTE: not implemented
+
+} else {
+	#panic("posix is unimplemented for the current target")
+}

+ 332 - 0
core/sys/posix/stdio.odin

@@ -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 - 0
core/sys/posix/stdlib.odin

@@ -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 - 0
core/sys/posix/string.odin

@@ -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 - 0
core/sys/posix/sys_ipc.odin

@@ -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 - 0
core/sys/posix/sys_mman.odin

@@ -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 - 0
core/sys/posix/sys_msg.odin

@@ -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 - 0
core/sys/posix/sys_resource.odin

@@ -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 - 0
core/sys/posix/sys_select.odin

@@ -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 - 0
core/sys/posix/sys_sem.odin

@@ -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 - 0
core/sys/posix/sys_shm.odin

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

+ 491 - 0
core/sys/posix/sys_socket.odin

@@ -0,0 +1,491 @@
+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 || ODIN_OS == .FreeBSD {
+		SO_LINGER   :: 0x1080
+		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")
+}
+

+ 540 - 0
core/sys/posix/sys_stat.odin

@@ -0,0 +1,540 @@
+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:
+
+	IFBLK  = log2(S_IFBLK),  /* Block special */
+	IFCHR  = log2(S_IFCHR),  /* Character special */
+	IFIFO  = log2(S_IFIFO),  /* FIFO special */
+	IFREG  = log2(S_IFREG),  /* Regular */
+	IFDIR  = log2(S_IFDIR),  /* Directory */
+	IFLNK  = log2(S_IFLNK),  /* Symbolic link */
+	IFSOCK = log2(S_IFSOCK), /* Socket */
+
+	// 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))
+
+// NOTE: making these `.IFREG in m` would probably be fine too,
+// but implementations make this an exclusive check so lets stick to it.
+
+_S_IFMT :: mode_t{ .IFBLK, .IFCHR, .IFIFO, .IFREG, .IFDIR, .IFLNK, .IFSOCK }
+
+// Test for a block special file.
+S_ISBLK :: #force_inline proc "contextless" (m: mode_t) -> bool {
+	return (m & _S_IFMT) == { .IFBLK }
+}
+
+// Test for a character special file.
+S_ISCHR :: #force_inline proc "contextless" (m: mode_t) -> bool {
+	return (m & _S_IFMT) == { .IFCHR }
+}
+
+// Test for a pipe or FIFO special file.
+S_ISFIFO :: #force_inline proc "contextless" (m: mode_t) -> bool {
+	return (m & _S_IFMT) == { .IFIFO }
+}
+
+// Test for a regular file.
+S_ISREG :: #force_inline proc "contextless" (m: mode_t) -> bool {
+	return (m & _S_IFMT) == { .IFREG }
+}
+
+// Test for a directory.
+S_ISDIR :: #force_inline proc "contextless" (m: mode_t) -> bool {
+	return (m & _S_IFMT) == { .IFDIR }
+}
+
+// Test for a symbolic link.
+S_ISLNK :: #force_inline proc "contextless" (m: mode_t) -> bool {
+	return (m & _S_IFMT) == { .IFLNK }
+}
+
+// Test for a socket.
+S_ISSOCK :: #force_inline proc "contextless" (m: mode_t) -> bool {
+	return (m & _S_IFMT) == { .IFSOCK }
+}
+
+// Test for a message queue.
+S_TYPEISMQ :: #force_inline proc "contextless" (m: mode_t) -> bool {
+	return _S_TYPEISMQ(m)
+}
+
+// Test for a semaphore.
+S_TYPEISSEM :: #force_inline proc "contextless" (m: mode_t) -> bool {
+	return _S_TYPEISSEM(m)
+}
+
+// Test for a shared memory object.
+S_TYPEISSHM :: #force_inline proc "contextless" (m: mode_t) -> bool {
+	return _S_TYPEISSHM(m)
+}
+
+// Test macro for a typed memory object.
+S_TYPEISTMO :: #force_inline proc "contextless" (m: mode_t) -> bool {
+	return _S_TYPEISTMO(m)
+}
+
+_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
+
+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 */
+	}
+
+	S_IFBLK  :: 0o060000
+	S_IFCHR  :: 0o020000
+	S_IFIFO  :: 0o010000
+	S_IFREG  :: 0o100000
+	S_IFDIR  :: 0o040000
+	S_IFLNK  :: 0o120000
+	S_IFSOCK :: 0o140000
+
+	__S_IFMT :: 0o170000
+
+	_S_TYPEISMQ :: #force_inline proc "contextless" (m: mode_t) -> bool {
+		return false
+	}
+
+	_S_TYPEISSEM :: #force_inline proc "contextless" (m: mode_t) -> bool {
+		return false
+	}
+
+	_S_TYPEISSHM :: #force_inline proc "contextless" (m: mode_t) -> bool {
+		return false
+	}
+
+	_S_TYPEISTMO :: #force_inline proc "contextless" (m: mode_t) -> bool {
+		return false
+	}
+
+	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,
+		}
+	}
+
+	S_IFBLK  :: 0o060000
+	S_IFCHR  :: 0o020000
+	S_IFIFO  :: 0o010000
+	S_IFREG  :: 0o100000
+	S_IFDIR  :: 0o040000
+	S_IFLNK  :: 0o120000
+	S_IFSOCK :: 0o140000
+
+	__S_IFMT :: 0o170000
+
+	_S_TYPEISMQ :: #force_inline proc "contextless" (m: mode_t) -> bool {
+		return false
+	}
+
+	_S_TYPEISSEM :: #force_inline proc "contextless" (m: mode_t) -> bool {
+		return false
+	}
+
+	_S_TYPEISSHM :: #force_inline proc "contextless" (m: mode_t) -> bool {
+		return false
+	}
+
+	_S_TYPEISTMO :: #force_inline proc "contextless" (m: mode_t) -> bool {
+		return false
+	}
+
+	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,
+	}
+
+	S_IFBLK  :: 0o060000
+	S_IFCHR  :: 0o020000
+	S_IFIFO  :: 0o010000
+	S_IFREG  :: 0o100000
+	S_IFDIR  :: 0o040000
+	S_IFLNK  :: 0o120000
+	S_IFSOCK :: 0o140000
+
+	__S_IFMT :: 0o170000
+
+	_S_TYPEISMQ :: #force_inline proc "contextless" (m: mode_t) -> bool {
+		return false
+	}
+
+	_S_TYPEISSEM :: #force_inline proc "contextless" (m: mode_t) -> bool {
+		return false
+	}
+
+	_S_TYPEISSHM :: #force_inline proc "contextless" (m: mode_t) -> bool {
+		return false
+	}
+
+	_S_TYPEISTMO :: #force_inline proc "contextless" (m: mode_t) -> bool {
+		return false
+	}
+
+	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,
+	}
+
+	S_IFBLK  :: 0o060000
+	S_IFCHR  :: 0o020000
+	S_IFIFO  :: 0o010000
+	S_IFREG  :: 0o100000
+	S_IFDIR  :: 0o040000
+	S_IFLNK  :: 0o120000
+	S_IFSOCK :: 0o140000
+
+	__S_IFMT :: 0o170000
+
+	_S_TYPEISMQ :: #force_inline proc "contextless" (m: mode_t) -> bool {
+		return false
+	}
+
+	_S_TYPEISSEM :: #force_inline proc "contextless" (m: mode_t) -> bool {
+		return false
+	}
+
+	_S_TYPEISSHM :: #force_inline proc "contextless" (m: mode_t) -> bool {
+		return false
+	}
+
+	_S_TYPEISTMO :: #force_inline proc "contextless" (m: mode_t) -> bool {
+		return false
+	}
+
+	UTIME_NOW  :: -2
+	UTIME_OMIT :: -1
+
+} else {
+	#panic("posix is unimplemented for the current target")
+}

+ 135 - 0
core/sys/posix/sys_statvfs.odin

@@ -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 - 0
core/sys/posix/sys_time.odin

@@ -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 - 0
core/sys/posix/sys_times.odin

@@ -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 - 0
core/sys/posix/sys_uio.odin

@@ -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 - 0
core/sys/posix/sys_un.odin

@@ -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 - 0
core/sys/posix/sys_utsname.odin

@@ -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 - 0
core/sys/posix/sys_wait.odin

@@ -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 (x >> 8) & 0x000000ff
+	}
+
+	@(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((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 - 0
core/sys/posix/termios.odin

@@ -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 - 0
core/sys/posix/time.odin

@@ -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 - 0
core/sys/posix/ulimit.odin

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

+ 1914 - 0
core/sys/posix/unistd.odin

@@ -0,0 +1,1914 @@
+package posix
+
+import "core:c"
+
+when ODIN_OS == .Darwin {
+	foreign import lib "system:System.framework"
+} else {
+	foreign import lib "system:c"
+}
+
+// unistd.h - standard symbolic constants and types
+
+foreign lib {
+	/*
+	Checks the file named by the pathname pointed to by the path argument for
+	accessibility according to the bit pattern contained in amode. 
+
+	Example:
+		if (posix.access("/tmp/myfile", posix.F_OK) != .OK) {
+			fmt.printfln("/tmp/myfile access check failed: %v", posix.strerror(posix.errno()))
+		}
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/access.html ]]
+	*/
+	access :: proc(path: cstring, amode: Mode_Flags = F_OK) -> result ---
+
+	/*
+	Equivalent to `access` but relative paths are resolved based on `fd`.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/access.html ]]
+	*/
+	faccessat :: proc(fd: FD, path: cstring, amode: Mode_Flags, flag: AT_Flags) -> result ---
+
+	/*
+	The alarm() function shall cause the system to generate a SIGALRM signal for the process after the number of realtime seconds specified by seconds have elapsed. Processor scheduling delays may prevent the process from handling the signal as soon as it is generated.
+
+	If seconds is 0, a pending alarm request, if any, is canceled.
+
+	Returns: the time left on the previous alarm() or 0
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/alarm.html ]]
+	*/
+	alarm :: proc(seconds: c.uint) -> c.uint ---
+
+	/*
+	Causes the directory named by path to become the current working directory.
+
+	Example:
+		if (posix.chdir("/tmp") == .OK) {
+			fmt.println("changed current directory to /tmp")
+		}
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/chdir.html ]]
+	*/
+	chdir :: proc(path: cstring) -> result ---
+
+	/*
+	Equivalent to chdir but instead of a path the fildes is resolved to a directory.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchdir.html ]]
+	*/
+	fchdir :: proc(fildes: FD) -> result ---
+
+	/*
+	Changes the user and group ownership of a file.
+
+	If owner or group is specified as (uid_t)-1 or (gid_t)-1, respectively, the corresponding ID of the file shall not be changed.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/chown.html ]]
+	*/
+	@(link_name=LCHOWN)
+	chown :: proc(path: cstring, owner: uid_t, group: gid_t) -> result ---
+
+	/*
+	Equivalent to chown expect that it takes a file descriptor.
+
+	Example:
+		fildes := posix.open("/home/cnd/mod1", {.RDWR})
+		pwd := posix.getpwnam("jones")
+		grp := posix.getgrnam("cnd")
+		posix.fchown(fildes, pwd.pw_uid, grp.gr_gid)
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchown.html ]]
+	*/
+	@(link_name=LFCHOWN)
+	fchown :: proc(fildes: FD, owner: uid_t, mode: gid_t) -> result ---
+
+	/*
+	Equivalent to fchown except that relative paths are based on the given fildes.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/chown.html ]]
+	*/
+	fchownat :: proc(fildes: FD, path: cstring, owner: uid_t, group: gid_t, flag: AT_Flags) -> result ---
+
+	/*
+	If path points to a symbolic link, the owner and group of the link itself is changed.
+	Equivalent to chown on normal files.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/lchown.html ]]
+	*/
+	@(link_name=LLCHOWN)
+	lchown :: proc(path: cstring, owner: uid_t, group: gid_t) -> result ---
+
+	/*
+	Deallocates the file descriptor indicated by fildes. 
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html ]]
+	*/
+	close :: proc(fildes: FD) -> result ---
+
+	/*
+	Return configuration-defined string values.
+	Its use and purpose are similar to sysconf(), but it is used where string values rather than numeric values are returned.
+
+	Returns: 0 (setting errno) if `name` is invalid, need `buf` `len` if buf is `nil`, amount of bytes added to buf otherwise
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/confstr.html ]]
+	*/
+	confstr :: proc(name: CS, buf: [^]c.char, len: c.size_t) -> c.size_t ---
+
+	/*
+	Determines the current value of a configurable limit or option that is associated with a file or directory.
+
+	Returns: value on success, -1 (setting errno) on failure, -1 (no errno) if the variable should be taken from limits
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fpathconf.html ]]
+	*/
+	pathconf :: proc(path: cstring, name: PC) -> c.long ---
+
+	/*
+	Equivalent to pathconf but takes a file descriptor instead of a path.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fpathconf.html ]]
+	*/
+	fpathconf :: proc(fildes: FD, name: PC) -> c.long ---
+
+	/*
+	Determines the current value of configurable system limit or options.
+
+	Returns: value on success, -1 (setting errno) on failure, -1 (no errno) if the variable should be taken from limits
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sysconf.html ]]
+	*/
+	sysconf :: proc(name: SC) -> c.long ---
+
+	/*
+	A string encoding function. The algorithm is implementation-defined.
+
+	The use of crypt() for anything other than password hashing is not recommended.
+
+	Returns: a static string overwritten by subsequent calls, `nil` (setting errno) on failure
+	*/
+	crypt :: proc(key: cstring, salt: cstring) -> cstring ---
+
+	/*
+	An implementation-defined encoding algorithm.
+	The key generated by setkey() is used to encrypt the string block with encrypt().
+
+	block must be 64 bytes.
+
+	decode controls if the block is encoded or decoded.
+
+	May set errno to ENOSYS if the functionality is not supported.
+
+	Example:
+		block: [64]byte
+		copy(block[:], "Hello, World!")
+
+		posix.set_errno(.NONE)
+		posix.encrypt(raw_data(block[:]), decode=false)
+		assert(posix.errno() == .NONE, "encrypt not supported")
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/encrypt.html ]]
+	*/
+	encrypt :: proc(block: [^]c.char, decode: b32) ---
+
+	/*
+	Returns a new file descriptor referring to the one given, sharing locks, clearing CLOEXEC.
+
+	Returns: -1 (setting errno) on failure, the new file descriptor on success
+
+	Example:
+		// Redirecting stdout to a file:
+		file := posix.open("/tmp/out", { .RDWR })
+		posix.close(1)
+		posix.dup(file)
+		posix.close(file)
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/dup.html ]] 
+	*/
+	dup :: proc(fildes: FD) -> FD ---
+
+	/*
+	Causes the file descriptor fildes2 to refer to the same open file description as
+	the file descriptor fildes and to share any locks, and shall return fildes2.
+
+	Returns: -1 (setting errno) on failure, fildes2 on success
+
+	Example:
+		// Redirecting stderr to stdout:
+		posix.dup2(1, 2)
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/dup.html ]] 
+	*/
+	dup2 :: proc(fildes, fildes2: FD) -> FD ---
+
+	/*
+	Exits but, shall not call functions registered with atexit() nor any registered signal handlers.
+	Open streams shall not be flushed.
+	Whether open streams are closed (without flushing) is implementation-defined. Finally, the calling process shall be terminated with the consequences described below.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/_exit.html ]]
+	*/
+	_exit :: proc(status: c.int) ---
+
+	/*
+	The exec family of functions shall replace the current process image with a new process image.
+	The new image shall be constructed from a regular, executable file called the new process image file.
+	There shall be no return from a successful exec,
+	because the calling process image is overlaid by the new process image.
+
+	Takes arguments as varargs and the last of them must be nil.
+
+	Example:
+		ret := posix.execl("/bin/ls", "ls", "-l", nil)
+		fmt.panicf("could not execute: %v %v", ret, posix.strerror(posix.errno()))
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/execl.html ]]
+	*/
+	execl :: proc(path: cstring, arg0: cstring, #c_vararg args: ..cstring) -> c.int ---
+
+	/*
+	The exec family of functions shall replace the current process image with a new process image.
+	The new image shall be constructed from a regular, executable file called the new process image file.
+	There shall be no return from a successful exec,
+	because the calling process image is overlaid by the new process image.
+
+	Takes arguments as varargs and the last of them must be nil.
+	After the arguments an array of environment strings (also nil terminated) is expected.
+
+	Example:
+		env := []cstring{
+			"HOME=/usr/home",
+			"LOGNAME=home",
+			nil,
+		}
+		ret := posix.execle("/bin/ls", "ls", cstring("-l"), cstring(nil), raw_data(env))
+		fmt.panicf("could not execute: %v", posix.strerror(posix.errno()))
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/execl.html ]]
+	*/
+	execle :: proc(path: cstring, arg0: cstring, #c_vararg args: ..any) -> c.int ---
+
+	/*
+	The exec family of functions shall replace the current process image with a new process image.
+	The new image shall be constructed from a regular, executable file called the new process image file.
+	There shall be no return from a successful exec,
+	because the calling process image is overlaid by the new process image.
+
+	If file does not contain a slash the PATH environment variable is searched for a matching file.
+	Takes arguments as varargs and the last of them must be nil.
+
+	Example:
+		ret := posix.execlp("ls", "-l", cstring(nil))
+		fmt.panicf("could not execute: %v, %v", ret, posix.strerror(posix.errno()))
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/execl.html ]]
+	*/
+	execlp :: proc(file: cstring, arg0: cstring, #c_vararg args: ..cstring) -> c.int ---
+
+	/*
+	The exec family of functions shall replace the current process image with a new process image.
+	The new image shall be constructed from a regular, executable file called the new process image file.
+	There shall be no return from a successful exec,
+	because the calling process image is overlaid by the new process image.
+
+	Takes arguments as an array which should be nil terminated.
+
+	Example:
+		args := []cstring{ "ls", "-l", nil }
+		ret := posix.execv("/bin/ls", raw_data(args))
+		fmt.panicf("could not execute: %v, %v", ret, posix.strerror(posix.errno()))
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/execl.html ]]
+	*/
+	execv :: proc(path: cstring, argv: [^]cstring) -> c.int ---
+
+	/*
+	The exec family of functions shall replace the current process image with a new process image.
+	The new image shall be constructed from a regular, executable file called the new process image file.
+	There shall be no return from a successful exec,
+	because the calling process image is overlaid by the new process image.
+
+	If file does not contain a slash the PATH environment variable is searched for a matching file.
+	Takes arguments as an array which should be nil terminated.
+
+	Example:
+		cmd := []cstring{ "ls", "-l", nil }
+		ret := posix.execvp("ls", raw_data(cmd))
+		fmt.panicf("could not execute: %v, %v", ret, posix.strerror(posix.errno()))
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/execl.html ]]
+	*/
+	execvp :: proc(file: cstring, argv: [^]cstring) -> c.int ---
+
+	/*
+	The exec family of functions shall replace the current process image with a new process image.
+	The new image shall be constructed from a regular, executable file called the new process image file.
+	There shall be no return from a successful exec,
+	because the calling process image is overlaid by the new process image.
+
+	Takes arguments as an array which should be nil terminated.
+	Takes environment variables as an array which should be nil terminated.
+
+	Example:
+		cmd := []cstring{ "ls", "-l", nil }
+		env := []cstring{ "HOME=/usr/home", "LOGNAME=home", nil }
+		ret := posix.execve("/bin/ls", raw_data(cmd), raw_data(env))
+		fmt.panicf("could not execute: %v, %v", ret, posix.strerror(posix.errno()))
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/execl.html ]]
+	*/
+	execve :: proc(path: cstring, argv: [^]cstring, envp: [^]cstring) -> c.int ---
+
+	/*
+	The exec family of functions shall replace the current process image with a new process image.
+	The new image shall be constructed from a regular, executable file called the new process image file.
+	There shall be no return from a successful exec,
+	because the calling process image is overlaid by the new process image.
+
+	Equivalent to execve but takes a file descriptor instead of a path.
+
+	Example:
+		ls := posix.open("/bin/ls", { .EXEC })
+		cmd := []cstring{ "ls", "-l", nil }
+		env := []cstring{ "HOME=/usr/home", "LOGNAME=home", nil }
+		ret := posix.fexecve(ls, raw_data(cmd), raw_data(env))
+		fmt.panicf("could not execute: %v, %v", ret, posix.strerror(posix.errno()))
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/execl.html ]]
+	*/
+	fexecve :: proc(fd: FD, argv: [^]cstring, envp: [^]cstring) -> c.int ---
+
+	/*
+	Example:
+		for i, entry := 0, posix.environ[0]; entry != nil; i, entry = i+1, posix.environ[i] {
+			fmt.println(entry)
+		}
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/execl.html ]]
+	*/
+	environ: [^]cstring
+
+	/*
+	Forcec all currently queued I/O operations associated with the file indicated by file descriptor
+	fildes to the synchronized I/O completion state.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fdatasync.html ]]
+	*/
+	fdatasync :: proc(fd: FD) -> result ---
+
+	/*
+	The fork() function shall create a new process.
+	The new process (child process) shall be an exact copy of the calling process (parent process).
+	With some exceptions outlined below.
+
+	Result: -1 (setting errno) on failure, otherwise 0 to the child process and the child process id to the parent process.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html ]]
+	*/
+	fork :: proc() -> pid_t ---
+
+	/*
+	Requests that all data for the open file descriptor named by fildes is to be transferred
+	to the storage device associated with the file described by fildes.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fsync.html ]]
+	*/
+	fsync :: proc(fildes: FD) -> result ---
+
+	/*
+	Truncates a file to the specified length.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/truncate.html ]]
+	*/
+	truncate :: proc(path: cstring, length: off_t) -> result ---
+
+	/*
+	Truncates a file to the specified length.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html ]]
+	*/
+	ftruncate :: proc(fildes: FD, length: off_t) -> result ---
+
+	/*
+	Places an absolute pathname of the current working directory into buf.
+
+	Returns: buf as a cstring on success, nil (setting errno) on failure
+
+	Example:
+		size: int
+		path_max := posix.pathconf(".", ._PATH_MAX)
+		if path_max == -1 {
+			size = 1024
+		} else if path_max > 10240 {
+			size = 10240
+		} else {
+			size = int(path_max)
+		}
+
+		buf: [dynamic]byte
+		cwd: cstring
+		for ; cwd == nil; size *= 2 {
+			if err := resize(&buf, size); err != nil {
+				fmt.panicf("allocation failure: %v", err)
+			}
+
+			cwd = posix.getcwd(raw_data(buf), len(buf))
+			if errno := posix.errno(); cwd == nil && errno != .ERANGE {
+				fmt.panicf("getcwd failure: %v", posix.strerror(errno))
+			}
+		}
+
+		fmt.println(path_max, cwd)
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getcwd.html ]]
+	*/
+	getcwd :: proc(buf: [^]c.char, size: c.size_t) -> cstring ---
+
+	/*
+	Returns the effective group ID of the calling process.
+
+	Returns: the ID, no failure is defined
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getegid.html ]]
+	*/
+	getegid :: proc() -> gid_t ---
+
+	/*
+	Returns the effective user ID of the calling process.
+
+	Returns: the ID, no failure is defined
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/geteuid.html ]]
+	*/
+	geteuid :: proc() -> uid_t ---
+
+	/*
+	Returns the real group ID of the calling process.
+
+	Returns: the ID, no failure is defined
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getgid.html ]]
+	*/
+	getgid :: proc() -> gid_t ---
+
+	/*
+	Fills the grouplist array with the current supplementary group IDs of the calling process.
+
+	Returns: -1 (setting errno) on failure, desired grouplist length if gidsetsize is 0, amount of IDs added otherwise
+
+	Example:
+		length := posix.getgroups(0, nil)
+		if length == -1 {
+			fmt.panicf("getgroups failure: %v", posix.strerror(posix.errno()))
+		}
+
+		groups := make([]posix.gid_t, length) or_else panic("allocation failure")
+		if posix.getgroups(length, raw_data(groups)) != length {
+			fmt.panicf("getgroups failure: %v", posix.strerror(posix.errno()))
+		}
+
+		fmt.println(groups)
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getgroups.html ]]
+	*/
+	getgroups :: proc(gidsetsize: c.int, grouplist: [^]gid_t) -> c.int ---
+
+	/*
+	Retrieves a 32-bit identifier for the current host.
+
+	Returns: the ID, no failure is defined
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/gethostid.html ]]
+	*/
+	gethostid :: proc() -> c.long ---
+
+	/*
+	Returns the standard host name for the current machine.
+
+	Host names are limited to HOST_NAME_MAX bytes.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/gethostname.html ]]
+	*/
+	gethostname :: proc(name: [^]c.char, namelen: c.size_t) -> result ---
+
+	/*
+	Returns a string containing the user name associated by the login activity.
+
+	Returns: nil (setting errno) on failure, the login name otherwise in a potentially static buffer overwritten by subsequent calls
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getlogin.html ]]
+	*/
+	getlogin :: proc() -> cstring ---
+
+	/*
+	Equivalent to getlogin but puts the name in the name buffer given.
+
+	The name is limited to LOGIN_NAME_MAX bytes.
+
+	Example:
+		max := posix.sysconf(posix._SC_LOGIN_NAME_MAX)+1
+		buf := make([]byte, max)
+		posix.getlogin_r(raw_data(buf), uint(len(max)))
+		fmt.printfln("login: %v", cstring(buf))
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getlogin.html ]]
+	*/
+	getlogin_r :: proc(name: [^]c.char, namelen: c.size_t) -> Errno ---
+
+	/*
+	A command-line parser, see linked docs.
+
+	Example:
+		// The following code fragment shows how you might process the arguments for a utility that
+		// can take the mutually-exclusive options a and b and the options f and o, both of which
+		// require arguments.
+
+		bflg, aflg, errflg: bool
+		ifile: string
+		ofile: string
+
+		for {
+			c := posix.getopt(i32(len(runtime.args__)), raw_data(runtime.args__), ":abf:o:")
+			(c != -1) or_break
+
+			switch c {
+			case 'a':
+				if bflg {
+					errflg = true
+				} else {
+					aflg = true
+				}
+			case 'b':
+				if aflg {
+					errflg = true
+				} else {
+					bflg = true
+				}
+			case 'f':
+				ifile = string(posix.optarg)
+			case 'o':
+				ofile = string(posix.optarg)
+			case ':': /* -f or -o without operand */
+				fmt.eprintfln("Option -%c requires an operand", posix.optopt)
+				errflg = true
+			case '?':
+				fmt.eprintfln("Unrecognized option: '-%c'", posix.optopt)
+				errflg = true
+			}
+		}
+
+		if errflg {
+			fmt.eprintfln("usage: . . . ")
+			posix.exit(2)
+		}
+
+		// Loop through remaining arguments:
+		for ; posix.optind < i32(len(runtime.args__)); posix.optind += 1 {
+			fmt.println(runtime.args__[posix.optind])
+		}
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getopt.html ]]
+	*/
+	getopt :: proc(argc: c.int, argv: [^]cstring, optstring: cstring) -> c.int ---
+
+	optarg: cstring
+	opterr: c.int
+	optind: c.int
+	optopt: c.int
+
+	/*
+	Returns the process group ID of the process whose process ID is equal to pid.
+	If pid is 0, it returns the process group ID of the calling process.
+
+	Returns: -1 on failure, the ID otherwise
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpgid.html ]]
+	*/
+	getpgid :: proc(pid: pid_t) -> pid_t ---
+
+	/*
+	Returns the process group ID of the calling process.
+
+	Returns: no failure
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpgrp.html ]]
+	*/
+	getpgrp :: proc() -> pid_t ---
+
+	/*
+	Returns the ID of the calling process.
+
+	Returns: no failure
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpid.html ]]
+	*/
+	getpid :: proc() -> pid_t ---
+
+	/*
+	Returns the parent process ID.
+
+	Returns: no failure
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getppid.html ]]
+	*/
+	getppid :: proc() -> pid_t ---
+
+
+	/*
+	Get the process group ID of the session leader.
+	If pid is 0, it is the current process.
+
+	Returns: -1 (setting errno) on failure, the pid otherwise
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsid.html ]]
+	*/
+	getsid :: proc(pid: pid_t) -> pid_t ---
+
+	/*
+	Returns the real user ID of the calling process.
+
+	Returns: no failure
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getuid.html ]]
+	*/
+	getuid :: proc() -> uid_t ---
+
+	/*
+	Tests whether fildes is associated with a terminal device.
+
+	Returns: false (setting errno) if fildes is invalid or not a terminal, true otherwise
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/isatty.html ]]
+	*/
+	isatty :: proc(fildes: FD) -> b32 ---
+
+	/*
+	Creates a new link for the existing file path1 to path2.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/link.html ]]
+	*/
+	link :: proc(path1: cstring, path2: cstring) -> result ---
+
+	/*
+	If path1 is relative it is relative to directory fd1.
+	If path2 is relative it is relative to directory fd2.
+	If flag is { .SYMLINK_FOLLOW } path1 is resolved to its link if it is a link.
+	Equivalent to link otherwise.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/link.html ]]
+	*/
+	linkat :: proc(fd1: FD, path1: cstring, fd2: FD, path2: cstring, flag: AT_Flags) -> result ---
+
+	/*
+	Creates a symbolic link called path2 that contains a link to path1.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/symlink.html ]]
+	*/
+	symlink :: proc(path1: cstring, path2: cstring) -> result ---
+
+	/*
+	Equivalent to symlink but relative paths are resolved to dir fd.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/symlink.html ]]
+	*/
+	symlinkat :: proc(path1: cstring, fd: FD, path2: cstring) -> result ---
+
+	/*
+	Locks sections of a file with advisory-mode locks.
+
+	Example:
+		fildes := posix.open("/home/cnd/mod1", { .RDWR })
+		if posix.lockf(fildes, .TLOCK, 10000) != .OK {
+			errno := posix.errno(); #partial switch errno {
+			case .EACCES, .EAGAIN:
+				// File is already locked.
+			case:
+				// Other error.
+				fmt.panicf("lockf failure: %v", posix.strerror(errno))
+			}
+		}
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/lockf.html ]]
+	*/
+	lockf :: proc(fildes: FD, function: Lock_Function, size: off_t) -> result ---
+
+	/*
+	Sets the file offset of the given file descriptor.
+
+	If whence is .SET, the offset is set
+	If whence is .CUR, the offset is the current offset + given offset
+	If whence is .END, the offset is set to the size of the file + given offset
+
+	Returns: the resulting offset or -1 (setting errno)
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/lseek.html ]]
+	*/
+	lseek :: proc(fildes: FD, offset: off_t, whence: Whence) -> off_t ---
+
+	/*
+	Changes the nice value of a process.
+
+	Higher values result in less favorable scheduling.
+
+	Because -1 is a valid nice value, checking failure would be done by first setting errno to .NONE
+	and then calling nice.
+
+	Returns: the new nice value, or -1 (setting) errno on failure
+
+	Example:
+		posix.set_errno(.NONE)
+		niceness := posix.nice(-20)
+		if errno := posix.errno(); niceness == -1 && errno != .NONE {
+			fmt.panicf("nice failure: %v", posix.strerror(errno))
+		}
+		fmt.printfln("Niceness is now: %v", niceness)
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/nice.html ]]
+	*/
+	nice :: proc(incr: c.int) -> c.int ---
+
+	/*
+	Suspend the thread until a signal is received.
+
+	Returns: -1 (setting errno to EINTR)
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pause.html ]]
+	*/
+	pause :: proc() -> c.int ---
+
+	/*
+	Create an interprocess channel.
+
+	Example:
+		fildes: [2]posix.FD
+		if posix.pipe(&fildes) != .OK {
+			// Handle error ...
+		}
+
+		switch posix.fork() {
+		case -1:
+			// Handle error ...
+
+		case 0: /* Child - reads from pipe */
+			BSIZE :: 100
+			buf: [BSIZE]byte
+			nbytes: int
+
+			posix.close(fildes[1])                                  /* Write end is unused */
+			nbytes = posix.read(fildes[0], raw_data(buf[:]), BSIZE) /* Get data from pipe */
+			/* At this point, a further read would see end-of-file ... */
+			posix.close(fildes[0])                                  /* Finished with pipe */
+
+			fmt.println(string(buf[:nbytes]))
+
+			posix.exit(0)
+
+		case: /* Parent - write to pipe */
+			msg := raw_data(transmute([]byte)string("Hello world\n"))
+			posix.close(fildes[0])           /* Read end is unused */
+			posix.write(fildes[1], msg, 12); /* Write data on pipe */
+			posix.close(fildes[1])
+			posix.exit(0)
+		}
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pipe.html ]]
+	*/
+	pipe :: proc(fildes: ^[2]FD) -> result ---
+
+	/*
+	Read from a file.
+
+	Returns: the amount of bytes read or -1 (setting errno) on failure
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pread.html ]]
+	*/
+	read :: proc(fd: FD, buf: [^]byte, nbyte: c.size_t) -> c.ssize_t ---
+
+	/*
+	Equivalent to read on a specified offset instead of the internal offset.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/pread.html ]]
+	*/
+	pread :: proc(fd: FD, buf: [^]byte, nbyte: c.size_t, offset: off_t) -> c.ssize_t ---
+
+	/*
+	Write on a file.
+
+	Returns: the amount of bytes written or -1 (setting errno) on failure.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html ]]
+	*/
+	write :: proc(fd: FD, buf: [^]byte, buflen: c.size_t) -> c.ssize_t ---
+
+	/*
+	Equivalent to write on a specified offset instead of the internal offset.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html ]]
+	*/
+	pwrite :: proc(fd: FD, buf: [^]byte, buflen: c.size_t, offset: off_t) -> c.ssize_t ---
+
+	/*
+	Read the contents of a symbolic link.
+
+	Returns: the amount of bytes read or -1 (setting errno) on failure.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/readlink.html ]]
+	*/
+	readlink :: proc(path: cstring, buf: [^]byte, bufsize: c.size_t) -> c.ssize_t ---
+
+	/*
+	Equivalent to readlink but relative paths are resolved based on the dir fd.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/readlink.html ]]
+	*/
+	readlinkat :: proc(fd: FD, path: cstring, buf: [^]byte, bufsize: c.size_t) -> c.ssize_t ---
+
+	/*
+	Remove an (empty) directory.
+
+	]] More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/rmdir.html ]]
+	*/
+	rmdir :: proc(path: cstring) -> result ---
+
+	/*
+	Set the effective group ID.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setegid.html ]]
+	*/
+	setegid :: proc(gid: gid_t) -> result ---
+
+	/*
+	Sets the effective user ID.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/seteuid.html ]]
+	*/
+	seteuid :: proc(uid: uid_t) -> result ---
+
+	/*
+	Sets the group ID.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setgid.html ]]
+	*/
+	setgid :: proc(gid: gid_t) -> result ---
+
+	/*
+	Set process group ID.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setpgid.html ]]
+	*/
+	setpgid :: proc(pid: pid_t, pgid: pid_t) -> result ---
+
+	/*
+	Set the process group ID to that of the process.
+
+	Returns: the process group id, no failures are defined
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setpgrp.html ]]
+	*/
+	setpgrp :: proc() -> pid_t ---
+
+	/*
+	Set the real and effective group IDs.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setregid.html ]]
+	*/
+	setregid :: proc(rgid: gid_t, egid: gid_t) -> result ---
+
+	/*
+	Set real and effective user IDs.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setreuid.html ]]
+	*/
+	setreuid :: proc(ruid: uid_t, euid: uid_t) -> result ---
+
+	/*
+	Create session and set process group ID.
+
+	Returns: the new process group ID or -1 (setting errno) on failure
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setsid.html ]]
+	*/
+	setsid :: proc() -> pid_t ---
+
+	/*
+	Set user ID.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setuid.html ]]
+	*/
+	setuid :: proc(uid: uid_t) -> result ---
+
+	/*
+	Suspend execution for an interval of time.
+
+	Returns: the time left to sleep (may be > 0 in case of signals)
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sleep.html ]]
+	*/
+	sleep :: proc(seconds: c.uint) -> c.uint ---
+
+	/*
+	Copy nbyte bytes, from src, to dest, exchanging adjecent bytes.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/swab.html ]]
+	*/
+	swab :: proc(src: [^]byte, dest: [^]byte, nbytes: c.ssize_t) ---
+
+	/*
+	Schedule file system updates.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sync.html ]]
+	*/
+	sync :: proc() ---
+
+	/*
+	Get the foreground process group ID.
+
+	Returns: -1 (setting errno) on failure, the id otherwise
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetpgrp.html ]]
+	*/
+	tcgetpgrp :: proc(fildes: FD) -> pid_t ---
+
+	/*
+	Set the foreground process group ID.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsetpgrp.html ]]
+	*/
+	tcsetpgrp :: proc(fildes: FD, pgid_id: pid_t) -> result ---
+
+	/*
+	Find the path name of a terminal.
+
+	Returns: nil (setting errno) on failure, the name, which may be invalidated by subsequent calls on success
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/ttyname.html ]]
+	*/
+	ttyname :: proc(fildes: FD) -> cstring ---
+
+	/*
+	Equivalent to ttyname but name is placed into the buf.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/ttyname.html ]]
+	*/
+	ttyname_r :: proc(fildes: FD, name: [^]byte, namesize: c.size_t) -> Errno ---
+
+	/*
+	Remove a directory entry.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlink.html ]]
+	*/
+	unlink :: proc(path: cstring) -> result ---
+
+	/*
+	Equivalent to unlink or rmdir (if flag is .REMOVEDIR) but relative paths are relative to the dir fd.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlink.html ]]
+	*/
+	unlinkat :: proc(fd: FD, path: cstring, flag: AT_Flags) -> result ---
+}
+
+STDERR_FILENO :: 2
+STDIN_FILENO  :: 0
+STDOUT_FILENO :: 1
+
+Mode_Flag_Bits :: enum c.int {
+	X_OK = log2(X_OK),
+	W_OK = log2(W_OK),
+	R_OK = log2(R_OK),
+}
+Mode_Flags :: bit_set[Mode_Flag_Bits; c.int]
+
+#assert(_F_OK == 0)
+F_OK :: Mode_Flags{}
+
+CS :: enum c.int {
+	_PATH                           = _CS_PATH,
+	_POSIX_V6_ILP32_OFF32_CFLAGS    = _CS_POSIX_V6_ILP32_OFF32_CFLAGS,
+	_POSIX_V6_ILP32_OFF32_LDFLAGS   = _CS_POSIX_V6_ILP32_OFF32_LDFLAGS,
+	_POSIX_V6_ILP32_OFF32_LIBS      = _CS_POSIX_V6_ILP32_OFF32_LIBS,
+	_POSIX_V6_ILP32_OFFBIG_CFLAGS   = _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS,
+	_POSIX_V6_ILP32_OFFBIG_LDFLAGS  = _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS,
+	_POSIX_V6_ILP32_OFFBIG_LIBS     = _CS_POSIX_V6_ILP32_OFFBIG_LIBS,
+	_POSIX_V6_LP64_OFF64_CFLAGS     = _CS_POSIX_V6_LP64_OFF64_CFLAGS,
+	_POSIX_V6_LP64_OFF64_LDFLAGS    = _CS_POSIX_V6_LP64_OFF64_LDFLAGS,
+	_POSIX_V6_LP64_OFF64_LIBS       = _CS_POSIX_V6_LP64_OFF64_LIBS,
+	_POSIX_V6_LPBIG_OFFBIG_CFLAGS   = _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS,
+	_POSIX_V6_LPBIG_OFFBIG_LDFLAGS  = _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS,
+	_POSIX_V6_LPBIG_OFFBIG_LIBS     = _CS_POSIX_V6_LPBIG_OFFBIG_LIBS,
+	_POSIX_V6_WIDTH_RESTRICTED_ENVS	= _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS,
+}
+
+PC :: enum c.int {
+	_2_SYMLINK          = _PC_2_SYMLINK,
+	_ALLOC_SIZE_MIN     = _PC_ALLOC_SIZE_MIN,
+	_ASYNC_IO           = _PC_ASYNC_IO,
+	_CHOWN_RESTRICTED   = _PC_CHOWN_RESTRICTED,
+	_FILESIZEBITS       = _PC_FILESIZEBITS,
+	_LINK_MAX           = _PC_LINK_MAX,
+	_MAX_CANON          = _PC_MAX_CANON,
+	_MAX_INPUT          = _PC_MAX_INPUT,
+	_NAME_MAX           = _PC_NAME_MAX,
+	_NO_TRUNC           = _PC_NO_TRUNC,
+	_PATH_MAX           = _PC_PATH_MAX,
+	_PIPE_BUF           = _PC_PIPE_BUF,
+	_PRIO_IO            = _PC_PRIO_IO,
+	_REC_INCR_XFER_SIZE = _PC_REC_INCR_XFER_SIZE,
+	_REC_MAX_XFER_SIZE  = _PC_REC_MAX_XFER_SIZE,
+	_REC_MIN_XFER_SIZE  = _PC_REC_MIN_XFER_SIZE,
+	_REC_XFER_ALIGN     = _PC_REC_XFER_ALIGN,
+	_SYMLINK_MAX        = _PC_SYMLINK_MAX,
+	_SYNC_IO            = _PC_SYNC_IO,
+	_VDISABLE           = _PC_VDISABLE,
+}
+
+SC :: enum c.int {
+	_2_C_BIND                     = _SC_2_C_BIND,
+	_2_C_DEV                      = _SC_2_C_DEV,
+	_2_CHAR_TERM                  = _SC_2_CHAR_TERM,
+	_2_FORT_DEV                   = _SC_2_FORT_DEV,
+	_2_FORT_RUN                   = _SC_2_FORT_RUN,
+	_2_LOCALEDEF                  = _SC_2_LOCALEDEF,
+	_2_PBS                        = _SC_2_PBS,
+	_2_PBS_ACCOUNTING             = _SC_2_PBS_ACCOUNTING,
+	_2_PBS_CHECKPOINT             = _SC_2_PBS_CHECKPOINT,
+	_2_PBS_LOCATE                 = _SC_2_PBS_LOCATE,
+	_2_PBS_MESSAGE                = _SC_2_PBS_MESSAGE,
+	_2_PBS_TRACK                  = _SC_2_PBS_TRACK,
+	_2_SW_DEV                     = _SC_2_SW_DEV,
+	_2_UPE                        = _SC_2_UPE,
+	_2_VERSION                    = _SC_2_VERSION,
+	_ADVISORY_INFO                = _SC_ADVISORY_INFO,
+	_AIO_LISTIO_MAX               = _SC_AIO_LISTIO_MAX,
+	_AIO_MAX                      = _SC_AIO_MAX,
+	_AIO_PRIO_DELTA_MAX           = _SC_AIO_PRIO_DELTA_MAX,
+	_ARG_MAX                      = _SC_ARG_MAX,
+	_ASYNCHRONOUS_IO              = _SC_ASYNCHRONOUS_IO,
+	_ATEXIT_MAX                   = _SC_ATEXIT_MAX,
+	_BARRIERS                     = _SC_BARRIERS,
+	_BC_BASE_MAX                  = _SC_BC_BASE_MAX,
+	_BC_DIM_MAX                   = _SC_BC_DIM_MAX,
+	_BC_SCALE_MAX                 = _SC_BC_SCALE_MAX,
+	_BC_STRING_MAX                = _SC_BC_STRING_MAX,
+	_CHILD_MAX                    = _SC_CHILD_MAX,
+	_CLK_TCK                      = _SC_CLK_TCK,
+	_CLOCK_SELECTION              = _SC_CLOCK_SELECTION,
+	_COLL_WEIGHTS_MAX             = _SC_COLL_WEIGHTS_MAX,
+	_CPUTIME                      = _SC_CPUTIME,
+	_DELAYTIMER_MAX               = _SC_DELAYTIMER_MAX,
+	_EXPR_NEST_MAX                = _SC_EXPR_NEST_MAX,
+	_FSYNC                        = _SC_FSYNC,
+	_GETGR_R_SIZE_MAX             = _SC_GETGR_R_SIZE_MAX,
+	_GETPW_R_SIZE_MAX             = _SC_GETPW_R_SIZE_MAX,
+	_HOST_NAME_MAX                = _SC_HOST_NAME_MAX,
+	_IOV_MAX                      = _SC_IOV_MAX,
+	_IPV6                         = _SC_IPV6,
+	_JOB_CONTROL                  = _SC_JOB_CONTROL,
+	_LINE_MAX                     = _SC_LINE_MAX,
+	_LOGIN_NAME_MAX               = _SC_LOGIN_NAME_MAX,
+	_MAPPED_FILES                 = _SC_MAPPED_FILES,
+	_MEMLOCK                      = _SC_MEMLOCK,
+	_MEMLOCK_RANGE                = _SC_MEMLOCK_RANGE,
+	_MEMORY_PROTECTION            = _SC_MEMORY_PROTECTION,
+	_MESSAGE_PASSING              = _SC_MESSAGE_PASSING,
+	_MONOTONIC_CLOCK              = _SC_MONOTONIC_CLOCK,
+	_MQ_OPEN_MAX                  = _SC_MQ_OPEN_MAX,
+	_MQ_PRIO_MAX                  = _SC_MQ_PRIO_MAX,
+	_NGROUPS_MAX                  = _SC_NGROUPS_MAX,
+	_OPEN_MAX                     = _SC_OPEN_MAX,
+	_PAGE_SIZE                    = _SC_PAGE_SIZE,
+	_PAGESIZE                     = _SC_PAGESIZE,
+	_PRIORITIZED_IO               = _SC_PRIORITIZED_IO,
+	_PRIORITY_SCHEDULING          = _SC_PRIORITY_SCHEDULING,
+	_RAW_SOCKETS                  = _SC_RAW_SOCKETS,
+	_RE_DUP_MAX                   = _SC_RE_DUP_MAX,
+	_READER_WRITER_LOCKS          = _SC_READER_WRITER_LOCKS,
+	_REALTIME_SIGNALS             = _SC_REALTIME_SIGNALS,
+	_REGEXP                       = _SC_REGEXP,
+	_RTSIG_MAX                    = _SC_RTSIG_MAX,
+	_SAVED_IDS                    = _SC_SAVED_IDS,
+	_SEM_NSEMS_MAX                = _SC_SEM_NSEMS_MAX,
+	_SEM_VALUE_MAX                = _SC_SEM_VALUE_MAX,
+	_SEMAPHORES                   = _SC_SEMAPHORES,
+	_SHARED_MEMORY_OBJECTS        = _SC_SHARED_MEMORY_OBJECTS,
+	_SHELL                        = _SC_SHELL,
+	_SIGQUEUE_MAX                 = _SC_SIGQUEUE_MAX,
+	_SPAWN                        = _SC_SPAWN,
+	_SPIN_LOCKS                   = _SC_SPIN_LOCKS,
+	_SPORADIC_SERVER              = _SC_SPORADIC_SERVER,
+	_SS_REPL_MAX                  = _SC_SS_REPL_MAX,
+	_STREAM_MAX                   = _SC_STREAM_MAX,
+	_SYMLOOP_MAX                  = _SC_SYMLOOP_MAX,
+	_SYNCHRONIZED_IO              = _SC_SYNCHRONIZED_IO,
+	_THREAD_ATTR_STACKADDR        = _SC_THREAD_ATTR_STACKADDR,
+	_THREAD_ATTR_STACKSIZE        = _SC_THREAD_ATTR_STACKSIZE,
+	_THREAD_CPUTIME               = _SC_THREAD_CPUTIME,
+	_THREAD_DESTRUCTOR_ITERATIONS = _SC_THREAD_DESTRUCTOR_ITERATIONS,
+	_THREAD_KEYS_MAX              = _SC_THREAD_KEYS_MAX,
+	_THREAD_PRIO_INHERIT          = _SC_THREAD_PRIO_INHERIT,
+	_THREAD_PRIO_PROTECT          = _SC_THREAD_PRIO_PROTECT,
+	_THREAD_PRIORITY_SCHEDULING   = _SC_THREAD_PRIORITY_SCHEDULING,
+	_THREAD_PROCESS_SHARED        = _SC_THREAD_PROCESS_SHARED,
+	_THREAD_SAFE_FUNCTIONS        = _SC_THREAD_SAFE_FUNCTIONS,
+	_THREAD_SPORADIC_SERVER       = _SC_THREAD_SPORADIC_SERVER,
+	_THREAD_STACK_MIN             = _SC_THREAD_STACK_MIN,
+	_THREAD_THREADS_MAX           = _SC_THREAD_THREADS_MAX,
+	_THREADS                      = _SC_THREADS,
+	_TIMEOUTS                     = _SC_TIMEOUTS,
+	_TIMER_MAX                    = _SC_TIMER_MAX,
+	_TIMERS                       = _SC_TIMERS,
+	_TRACE                        = _SC_TRACE,
+	_TRACE_EVENT_FILTER           = _SC_TRACE_EVENT_FILTER,
+	_TRACE_EVENT_NAME_MAX         = _SC_TRACE_EVENT_NAME_MAX,
+	_TRACE_INHERIT                = _SC_TRACE_INHERIT,
+	_TRACE_LOG                    = _SC_TRACE_LOG,
+	_TRACE_NAME_MAX               = _SC_TRACE_NAME_MAX,
+	_TRACE_SYS_MAX                = _SC_TRACE_SYS_MAX,
+	_TRACE_USER_EVENT_MAX         = _SC_TRACE_USER_EVENT_MAX,
+	_TTY_NAME_MAX                 = _SC_TTY_NAME_MAX,
+	_TYPED_MEMORY_OBJECTS         = _SC_TYPED_MEMORY_OBJECTS,
+	_TZNAME_MAX                   = _SC_TZNAME_MAX,
+	_V6_ILP32_OFF32               = _SC_V6_ILP32_OFF32,
+	_V6_ILP32_OFFBIG              = _SC_V6_ILP32_OFFBIG,
+	_V6_LP64_OFF64                = _SC_V6_LP64_OFF64,
+	_V6_LPBIG_OFFBIG              = _SC_V6_LPBIG_OFFBIG,
+	_VERSION                      = _SC_VERSION,
+	_XOPEN_CRYPT                  = _SC_XOPEN_CRYPT,
+	_XOPEN_ENH_I18N               = _SC_XOPEN_ENH_I18N,
+	_XOPEN_REALTIME               = _SC_XOPEN_REALTIME,
+	_XOPEN_REALTIME_THREADS       = _SC_XOPEN_REALTIME_THREADS,
+	_XOPEN_SHM                    = _SC_XOPEN_SHM,
+	_XOPEN_STREAMS                = _SC_XOPEN_STREAMS,
+	_XOPEN_UNIX                   = _SC_XOPEN_UNIX,
+	_XOPEN_VERSION                = _SC_XOPEN_VERSION,
+}
+
+Lock_Function :: enum c.int {
+	// Lock a section for exclusive use.
+	LOCK  = F_LOCK,
+	// Test a section for locks by other processes.
+	TEST  = F_TEST,
+	// Test and lock a section for exclusive use.
+	TLOCK = F_TLOCK,
+	// Unlock locked sections.
+	ULOCK = F_ULOCK,
+}
+
+when ODIN_OS == .NetBSD {
+	@(private) LCHOWN  :: "__posix_chown"
+	@(private) LFCHOWN :: "__posix_fchown"
+	@(private) LLCHOWN :: "__posix_lchown"
+} else {
+	@(private) LCHOWN  :: "chown"
+	@(private) LFCHOWN :: "fchown"
+	@(private) LLCHOWN :: "lchown"
+}
+
+when ODIN_OS == .Darwin {
+
+	_F_OK :: 0
+	X_OK :: (1<<0)
+	W_OK :: (1<<1)
+	R_OK :: (1<<2)
+
+	F_LOCK  :: 1
+	F_TEST  :: 3
+	F_TLOCK :: 2
+	F_ULOCK :: 0
+
+	_CS_PATH                            :: 1
+	_CS_POSIX_V6_ILP32_OFF32_CFLAGS		:: 2
+	_CS_POSIX_V6_ILP32_OFF32_LDFLAGS	:: 3
+	_CS_POSIX_V6_ILP32_OFF32_LIBS		:: 4
+	_CS_POSIX_V6_ILP32_OFFBIG_CFLAGS	:: 5
+	_CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS	:: 6
+	_CS_POSIX_V6_ILP32_OFFBIG_LIBS		:: 7
+	_CS_POSIX_V6_LP64_OFF64_CFLAGS		:: 8
+	_CS_POSIX_V6_LP64_OFF64_LDFLAGS		:: 9
+	_CS_POSIX_V6_LP64_OFF64_LIBS		:: 10
+	_CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS	:: 11
+	_CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS	:: 12
+	_CS_POSIX_V6_LPBIG_OFFBIG_LIBS		:: 13
+	_CS_POSIX_V6_WIDTH_RESTRICTED_ENVS	:: 14
+
+	_PC_LINK_MAX           :: 1
+	_PC_MAX_CANON          :: 2
+	_PC_MAX_INPUT          :: 3
+	_PC_NAME_MAX           :: 4
+	_PC_PATH_MAX           :: 5
+	_PC_PIPE_BUF           :: 6
+	_PC_CHOWN_RESTRICTED   :: 7
+	_PC_NO_TRUNC           :: 8
+	_PC_VDISABLE           :: 9
+	_PC_2_SYMLINK          :: 15
+	_PC_ALLOC_SIZE_MIN     :: 16
+	_PC_ASYNC_IO           :: 17
+	_PC_FILESIZEBITS       :: 18
+	_PC_PRIO_IO            :: 19
+	_PC_REC_INCR_XFER_SIZE :: 20
+	_PC_REC_MAX_XFER_SIZE  :: 21
+	_PC_REC_MIN_XFER_SIZE  :: 22
+	_PC_REC_XFER_ALIGN     :: 23
+	_PC_SYMLINK_MAX        :: 24
+	_PC_SYNC_IO            :: 25
+
+	_SC_ARG_MAX                      :: 1
+	_SC_CHILD_MAX                    :: 2
+	_SC_CLK_TCK                      :: 3
+	_SC_NGROUPS_MAX                  :: 4
+	_SC_OPEN_MAX                     :: 5
+	_SC_JOB_CONTROL                  :: 6
+	_SC_SAVED_IDS                    :: 7
+	_SC_VERSION                      :: 8
+	_SC_BC_BASE_MAX                  :: 9
+
+	_SC_BC_DIM_MAX                   :: 10
+	_SC_BC_SCALE_MAX                 :: 11
+	_SC_BC_STRING_MAX                :: 12
+	_SC_COLL_WEIGHTS_MAX             :: 13
+	_SC_EXPR_NEST_MAX                :: 14
+	_SC_LINE_MAX                     :: 15
+	_SC_RE_DUP_MAX                   :: 16
+	_SC_2_VERSION                    :: 17
+	_SC_2_C_BIND                     :: 18
+	_SC_2_C_DEV                      :: 19
+
+	_SC_2_CHAR_TERM                  :: 20
+	_SC_2_FORT_DEV                   :: 21
+	_SC_2_FORT_RUN                   :: 22
+	_SC_2_LOCALEDEF                  :: 23
+	_SC_2_SW_DEV                     :: 24
+	_SC_2_UPE                        :: 25
+	_SC_STREAM_MAX                   :: 26
+	_SC_TZNAME_MAX                   :: 27
+	_SC_ASYNCHRONOUS_IO              :: 28
+	_SC_PAGE_SIZE                    :: 29
+	_SC_PAGESIZE                     :: _SC_PAGE_SIZE
+
+	_SC_MEMLOCK                      :: 30
+	_SC_MEMLOCK_RANGE                :: 31
+	_SC_MEMORY_PROTECTION            :: 32
+	_SC_MESSAGE_PASSING              :: 33
+	_SC_PRIORITIZED_IO               :: 34
+	_SC_PRIORITY_SCHEDULING          :: 35
+	_SC_REALTIME_SIGNALS             :: 36
+	_SC_SEMAPHORES                   :: 37
+	_SC_FSYNC                        :: 38
+	_SC_SHARED_MEMORY_OBJECTS        :: 39
+
+	_SC_SYNCHRONIZED_IO              :: 40
+	_SC_TIMERS                       :: 41
+	_SC_AIO_LISTIO_MAX               :: 42
+	_SC_AIO_MAX                      :: 43
+	_SC_AIO_PRIO_DELTA_MAX           :: 44
+	_SC_DELAYTIMER_MAX               :: 45
+	_SC_MQ_OPEN_MAX                  :: 46
+	_SC_MAPPED_FILES                 :: 47
+	_SC_RTSIG_MAX                    :: 48
+	_SC_SEM_NSEMS_MAX                :: 49
+
+	_SC_SEM_VALUE_MAX                :: 50
+	_SC_SIGQUEUE_MAX                 :: 51
+	_SC_TIMER_MAX                    :: 52
+	_SC_IOV_MAX                      :: 56
+	_SC_2_PBS                        :: 59
+
+	_SC_2_PBS_ACCOUNTING             :: 60
+	_SC_2_PBS_CHECKPOINT             :: 61
+	_SC_2_PBS_LOCATE                 :: 62
+	_SC_2_PBS_MESSAGE                :: 63
+	_SC_2_PBS_TRACK                  :: 64
+	_SC_ADVISORY_INFO                :: 65
+	_SC_BARRIERS                     :: 66
+	_SC_CLOCK_SELECTION              :: 67
+	_SC_CPUTIME                      :: 68
+
+	_SC_GETGR_R_SIZE_MAX             :: 70
+	_SC_GETPW_R_SIZE_MAX             :: 71
+	_SC_HOST_NAME_MAX                :: 72
+	_SC_LOGIN_NAME_MAX               :: 73
+	_SC_MONOTONIC_CLOCK              :: 74
+	_SC_MQ_PRIO_MAX                  :: 75
+	_SC_READER_WRITER_LOCKS          :: 76
+	_SC_REGEXP                       :: 77
+	_SC_SHELL                        :: 78
+	_SC_SPAWN                        :: 79
+
+	_SC_SPIN_LOCKS                   :: 80
+	_SC_SPORADIC_SERVER              :: 81
+	_SC_THREAD_ATTR_STACKADDR        :: 82
+	_SC_THREAD_ATTR_STACKSIZE        :: 83
+	_SC_THREAD_CPUTIME               :: 84
+	_SC_THREAD_DESTRUCTOR_ITERATIONS :: 85
+	_SC_THREAD_KEYS_MAX              :: 86
+	_SC_THREAD_PRIO_INHERIT          :: 87
+	_SC_THREAD_PRIO_PROTECT          :: 88
+	_SC_THREAD_PRIORITY_SCHEDULING   :: 89
+
+	_SC_THREAD_PROCESS_SHARED        :: 90
+	_SC_THREAD_SAFE_FUNCTIONS        :: 91
+	_SC_THREAD_SPORADIC_SERVER       :: 92
+	_SC_THREAD_STACK_MIN             :: 93
+	_SC_THREAD_THREADS_MAX           :: 94
+	_SC_TIMEOUTS                     :: 95
+	_SC_THREADS                      :: 96
+	_SC_TRACE                        :: 97
+	_SC_TRACE_EVENT_FILTER           :: 98
+	_SC_TRACE_INHERIT                :: 99
+
+	_SC_TRACE_LOG                    :: 100
+	_SC_TTY_NAME_MAX                 :: 101
+	_SC_TYPED_MEMORY_OBJECTS         :: 102
+	_SC_V6_ILP32_OFF32               :: 103
+	_SC_V6_ILP32_OFFBIG              :: 104
+	_SC_V6_LP64_OFF64                :: 105
+	_SC_V6_LPBIG_OFFBIG              :: 106
+	_SC_ATEXIT_MAX                   :: 107
+	_SC_XOPEN_CRYPT                  :: 108
+	_SC_XOPEN_ENH_I18N               :: 109
+
+	_SC_XOPEN_REALTIME               :: 111
+	_SC_XOPEN_REALTIME_THREADS       :: 112
+	_SC_XOPEN_SHM                    :: 113
+	_SC_XOPEN_STREAMS                :: 114
+	_SC_XOPEN_UNIX                   :: 115
+	_SC_XOPEN_VERSION                :: 116
+	_SC_IPV6                         :: 118
+	_SC_RAW_SOCKETS                  :: 119
+
+	_SC_SYMLOOP_MAX                  :: 120
+	_SC_SS_REPL_MAX                  :: 126
+	_SC_TRACE_EVENT_NAME_MAX         :: 127
+	_SC_TRACE_NAME_MAX               :: 128
+	_SC_TRACE_SYS_MAX                :: 129
+	_SC_TRACE_USER_EVENT_MAX         :: 130
+
+	_POSIX_VDISABLE :: '\377'
+
+} else when ODIN_OS == .FreeBSD {
+
+	_F_OK :: 0
+	X_OK :: 0x01
+	W_OK :: 0x02
+	R_OK :: 0x04
+
+	F_LOCK  :: 1
+	F_TEST  :: 3
+	F_TLOCK :: 2
+	F_ULOCK :: 0
+
+	_CS_PATH                            :: 1
+	_CS_POSIX_V6_ILP32_OFF32_CFLAGS		:: 2
+	_CS_POSIX_V6_ILP32_OFF32_LDFLAGS	:: 3
+	_CS_POSIX_V6_ILP32_OFF32_LIBS		:: 4
+	_CS_POSIX_V6_ILP32_OFFBIG_CFLAGS	:: 5
+	_CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS	:: 6
+	_CS_POSIX_V6_ILP32_OFFBIG_LIBS		:: 7
+	_CS_POSIX_V6_LP64_OFF64_CFLAGS		:: 8
+	_CS_POSIX_V6_LP64_OFF64_LDFLAGS		:: 9
+	_CS_POSIX_V6_LP64_OFF64_LIBS		:: 10
+	_CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS	:: 11
+	_CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS	:: 12
+	_CS_POSIX_V6_LPBIG_OFFBIG_LIBS		:: 13
+	_CS_POSIX_V6_WIDTH_RESTRICTED_ENVS	:: 14
+
+	_PC_LINK_MAX           :: 1
+	_PC_MAX_CANON          :: 2
+	_PC_MAX_INPUT          :: 3
+	_PC_NAME_MAX           :: 4
+	_PC_PATH_MAX           :: 5
+	_PC_PIPE_BUF           :: 6
+	_PC_CHOWN_RESTRICTED   :: 7
+	_PC_NO_TRUNC           :: 8
+	_PC_VDISABLE           :: 9
+	_PC_2_SYMLINK          :: 13 // NOTE: not in headers (freebsd)
+	_PC_ALLOC_SIZE_MIN     :: 10
+	_PC_ASYNC_IO           :: 53
+	_PC_FILESIZEBITS       :: 12
+	_PC_PRIO_IO            :: 54
+	_PC_REC_INCR_XFER_SIZE :: 14
+	_PC_REC_MAX_XFER_SIZE  :: 15
+	_PC_REC_MIN_XFER_SIZE  :: 16
+	_PC_REC_XFER_ALIGN     :: 17
+	_PC_SYMLINK_MAX        :: 18
+	_PC_SYNC_IO            :: 55
+
+	_SC_ARG_MAX                      :: 1
+	_SC_CHILD_MAX                    :: 2
+	_SC_CLK_TCK                      :: 3
+	_SC_NGROUPS_MAX                  :: 4
+	_SC_OPEN_MAX                     :: 5
+	_SC_JOB_CONTROL                  :: 6
+	_SC_SAVED_IDS                    :: 7
+	_SC_VERSION                      :: 8
+	_SC_BC_BASE_MAX                  :: 9
+
+	_SC_BC_DIM_MAX                   :: 10
+	_SC_BC_SCALE_MAX                 :: 11
+	_SC_BC_STRING_MAX                :: 12
+	_SC_COLL_WEIGHTS_MAX             :: 13
+	_SC_EXPR_NEST_MAX                :: 14
+	_SC_LINE_MAX                     :: 15
+	_SC_RE_DUP_MAX                   :: 16
+	_SC_2_VERSION                    :: 17
+	_SC_2_C_BIND                     :: 18
+	_SC_2_C_DEV                      :: 19
+
+	_SC_2_CHAR_TERM                  :: 20
+	_SC_2_FORT_DEV                   :: 21
+	_SC_2_FORT_RUN                   :: 22
+	_SC_2_LOCALEDEF                  :: 23
+	_SC_2_SW_DEV                     :: 24
+	_SC_2_UPE                        :: 25
+	_SC_STREAM_MAX                   :: 26
+	_SC_TZNAME_MAX                   :: 27
+	_SC_ASYNCHRONOUS_IO              :: 28
+	_SC_MAPPED_FILES                 :: 29
+
+	_SC_MEMLOCK                      :: 30
+	_SC_MEMLOCK_RANGE                :: 31
+	_SC_MEMORY_PROTECTION            :: 32
+	_SC_MESSAGE_PASSING              :: 33
+	_SC_PRIORITIZED_IO               :: 34
+	_SC_PRIORITY_SCHEDULING          :: 35
+	_SC_REALTIME_SIGNALS             :: 36
+	_SC_SEMAPHORES                   :: 37
+	_SC_FSYNC                        :: 38
+	_SC_SHARED_MEMORY_OBJECTS        :: 39
+
+	_SC_SYNCHRONIZED_IO              :: 40
+	_SC_TIMERS                       :: 41
+	_SC_AIO_LISTIO_MAX               :: 42
+	_SC_AIO_MAX                      :: 43
+	_SC_AIO_PRIO_DELTA_MAX           :: 44
+	_SC_DELAYTIMER_MAX               :: 45
+	_SC_MQ_OPEN_MAX                  :: 46
+	_SC_PAGE_SIZE                    :: 47
+	_SC_PAGESIZE                     :: _SC_PAGE_SIZE
+	_SC_RTSIG_MAX                    :: 48
+	_SC_SEM_NSEMS_MAX                :: 49
+
+	_SC_SEM_VALUE_MAX                :: 50
+	_SC_SIGQUEUE_MAX                 :: 51
+	_SC_TIMER_MAX                    :: 52
+	_SC_IOV_MAX                      :: 56
+	_SC_2_PBS                        :: 59
+
+	_SC_2_PBS_ACCOUNTING             :: 60
+	_SC_2_PBS_CHECKPOINT             :: 61
+	_SC_2_PBS_LOCATE                 :: 62
+	_SC_2_PBS_MESSAGE                :: 63
+	_SC_2_PBS_TRACK                  :: 64
+	_SC_ADVISORY_INFO                :: 65
+	_SC_BARRIERS                     :: 66
+	_SC_CLOCK_SELECTION              :: 67
+	_SC_CPUTIME                      :: 68
+
+	_SC_GETGR_R_SIZE_MAX             :: 70
+	_SC_GETPW_R_SIZE_MAX             :: 71
+	_SC_HOST_NAME_MAX                :: 72
+	_SC_LOGIN_NAME_MAX               :: 73
+	_SC_MONOTONIC_CLOCK              :: 74
+	_SC_MQ_PRIO_MAX                  :: 75
+	_SC_READER_WRITER_LOCKS          :: 76
+	_SC_REGEXP                       :: 77
+	_SC_SHELL                        :: 78
+	_SC_SPAWN                        :: 79
+
+	_SC_SPIN_LOCKS                   :: 80
+	_SC_SPORADIC_SERVER              :: 81
+	_SC_THREAD_ATTR_STACKADDR        :: 82
+	_SC_THREAD_ATTR_STACKSIZE        :: 83
+	_SC_THREAD_CPUTIME               :: 84
+	_SC_THREAD_DESTRUCTOR_ITERATIONS :: 85
+	_SC_THREAD_KEYS_MAX              :: 86
+	_SC_THREAD_PRIO_INHERIT          :: 87
+	_SC_THREAD_PRIO_PROTECT          :: 88
+	_SC_THREAD_PRIORITY_SCHEDULING   :: 89
+
+	_SC_THREAD_PROCESS_SHARED        :: 90
+	_SC_THREAD_SAFE_FUNCTIONS        :: 91
+	_SC_THREAD_SPORADIC_SERVER       :: 92
+	_SC_THREAD_STACK_MIN             :: 93
+	_SC_THREAD_THREADS_MAX           :: 94
+	_SC_TIMEOUTS                     :: 95
+	_SC_THREADS                      :: 96
+	_SC_TRACE                        :: 97
+	_SC_TRACE_EVENT_FILTER           :: 98
+	_SC_TRACE_INHERIT                :: 99
+
+	_SC_TRACE_LOG                    :: 100
+	_SC_TTY_NAME_MAX                 :: 101
+	_SC_TYPED_MEMORY_OBJECTS         :: 102
+	_SC_V6_ILP32_OFF32               :: 103
+	_SC_V6_ILP32_OFFBIG              :: 104
+	_SC_V6_LP64_OFF64                :: 105
+	_SC_V6_LPBIG_OFFBIG              :: 106
+	_SC_ATEXIT_MAX                   :: 107
+	_SC_XOPEN_CRYPT                  :: 108
+	_SC_XOPEN_ENH_I18N               :: 109
+
+	_SC_XOPEN_REALTIME               :: 111
+	_SC_XOPEN_REALTIME_THREADS       :: 112
+	_SC_XOPEN_SHM                    :: 113
+	_SC_XOPEN_STREAMS                :: 114
+	_SC_XOPEN_UNIX                   :: 115
+	_SC_XOPEN_VERSION                :: 116
+	_SC_IPV6                         :: 118
+	_SC_RAW_SOCKETS                  :: 119
+
+	_SC_SYMLOOP_MAX                  :: 120
+	_SC_SS_REPL_MAX                  :: 126 // NOTE: not in headers
+	_SC_TRACE_EVENT_NAME_MAX         :: 127 // NOTE: not in headers
+	_SC_TRACE_NAME_MAX               :: 128 // NOTE: not in headers
+	_SC_TRACE_SYS_MAX                :: 129 // NOTE: not in headers
+	_SC_TRACE_USER_EVENT_MAX         :: 130 // NOTE: not in headers
+
+	_POSIX_VDISABLE :: 0xff
+
+} else when ODIN_OS == .NetBSD {
+
+	_F_OK :: 0
+	X_OK :: 0x01
+	W_OK :: 0x02
+	R_OK :: 0x04
+
+	F_LOCK  :: 1
+	F_TEST  :: 3
+	F_TLOCK :: 2
+	F_ULOCK :: 0
+
+	_CS_PATH                            :: 1
+	_CS_POSIX_V6_ILP32_OFF32_CFLAGS		:: 2
+	_CS_POSIX_V6_ILP32_OFF32_LDFLAGS	:: 3
+	_CS_POSIX_V6_ILP32_OFF32_LIBS		:: 4
+	_CS_POSIX_V6_ILP32_OFFBIG_CFLAGS	:: 5
+	_CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS	:: 6
+	_CS_POSIX_V6_ILP32_OFFBIG_LIBS		:: 7
+	_CS_POSIX_V6_LP64_OFF64_CFLAGS		:: 8
+	_CS_POSIX_V6_LP64_OFF64_LDFLAGS		:: 9
+	_CS_POSIX_V6_LP64_OFF64_LIBS		:: 10
+	_CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS	:: 11
+	_CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS	:: 12
+	_CS_POSIX_V6_LPBIG_OFFBIG_LIBS		:: 13
+	_CS_POSIX_V6_WIDTH_RESTRICTED_ENVS	:: 14
+
+	_PC_LINK_MAX           :: 1
+	_PC_MAX_CANON          :: 2
+	_PC_MAX_INPUT          :: 3
+	_PC_NAME_MAX           :: 4
+	_PC_PATH_MAX           :: 5
+	_PC_PIPE_BUF           :: 6
+	_PC_CHOWN_RESTRICTED   :: 7
+	_PC_NO_TRUNC           :: 8
+	_PC_VDISABLE           :: 9
+	_PC_2_SYMLINK          :: 13 // NOTE: not in headers
+	_PC_ALLOC_SIZE_MIN     :: 10 // NOTE: not in headers
+	_PC_ASYNC_IO           :: 53 // NOTE: not in headers
+	_PC_FILESIZEBITS       :: 11
+	_PC_PRIO_IO            :: 54 // NOTE: not in headers
+	_PC_REC_INCR_XFER_SIZE :: 14 // NOTE: not in headers
+	_PC_REC_MAX_XFER_SIZE  :: 15 // NOTE: not in headers
+	_PC_REC_MIN_XFER_SIZE  :: 16 // NOTE: not in headers
+	_PC_REC_XFER_ALIGN     :: 17 // NOTE: not in headers
+	_PC_SYMLINK_MAX        :: 12
+	_PC_SYNC_IO            :: 10
+
+	_SC_ARG_MAX                      :: 1
+	_SC_CHILD_MAX                    :: 2
+	_SC_NGROUPS_MAX                  :: 4
+	_SC_OPEN_MAX                     :: 5
+	_SC_JOB_CONTROL                  :: 6
+	_SC_SAVED_IDS                    :: 7
+	_SC_VERSION                      :: 8
+	_SC_BC_BASE_MAX                  :: 9
+
+	_SC_BC_DIM_MAX                   :: 10
+	_SC_BC_SCALE_MAX                 :: 11
+	_SC_BC_STRING_MAX                :: 12
+	_SC_COLL_WEIGHTS_MAX             :: 13
+	_SC_EXPR_NEST_MAX                :: 14
+	_SC_LINE_MAX                     :: 15
+	_SC_RE_DUP_MAX                   :: 16
+	_SC_2_VERSION                    :: 17
+	_SC_2_C_BIND                     :: 18
+	_SC_2_C_DEV                      :: 19
+
+	_SC_2_CHAR_TERM                  :: 20
+	_SC_2_FORT_DEV                   :: 21
+	_SC_2_FORT_RUN                   :: 22
+	_SC_2_LOCALEDEF                  :: 23
+	_SC_2_SW_DEV                     :: 24
+	_SC_2_UPE                        :: 25
+	_SC_STREAM_MAX                   :: 26
+	_SC_TZNAME_MAX                   :: 27
+	_SC_PAGE_SIZE                    :: 28
+	_SC_PAGESIZE                     :: _SC_PAGE_SIZE
+	_SC_FSYNC                        :: 29
+
+	_SC_XOPEN_SHM                    :: 30
+	_SC_SYNCHRONIZED_IO              :: 31
+	_SC_IOV_MAX                      :: 32
+	_SC_MAPPED_FILES                 :: 33
+	_SC_MEMLOCK                      :: 34
+	_SC_MEMLOCK_RANGE                :: 35
+	_SC_MEMORY_PROTECTION            :: 36
+	_SC_LOGIN_NAME_MAX               :: 37
+	_SC_MONOTONIC_CLOCK              :: 38
+	_SC_CLK_TCK                      :: 39
+
+	_SC_ATEXIT_MAX                   :: 40
+	_SC_THREADS                      :: 41
+	_SC_SEMAPHORES                   :: 42
+	_SC_BARRIERS                     :: 43
+	_SC_TIMERS                       :: 44
+	_SC_SPIN_LOCKS                   :: 45
+	_SC_READER_WRITER_LOCKS          :: 46
+	_SC_GETGR_R_SIZE_MAX             :: 47
+	_SC_GETPW_R_SIZE_MAX             :: 48
+	_SC_CLOCK_SELECTION              :: 49
+
+	_SC_ASYNCHRONOUS_IO              :: 50
+	_SC_AIO_LISTIO_MAX               :: 51
+	_SC_AIO_MAX                      :: 52
+	_SC_MESSAGE_PASSING              :: 53
+	_SC_MQ_OPEN_MAX                  :: 54
+	_SC_MQ_PRIO_MAX                  :: 55
+	_SC_PRIORITY_SCHEDULING          :: 56
+	_SC_THREAD_DESTRUCTOR_ITERATIONS :: 57
+	_SC_THREAD_KEYS_MAX              :: 58
+	_SC_THREAD_STACK_MIN             :: 59
+
+	_SC_THREAD_THREADS_MAX           :: 60
+	_SC_THREAD_ATTR_STACKADDR        :: 61
+	_SC_THREAD_ATTR_STACKSIZE        :: 62
+	_SC_THREAD_PRIORITY_SCHEDULING   :: 63
+	_SC_THREAD_PRIO_INHERIT          :: 64
+	_SC_THREAD_PRIO_PROTECT          :: 65
+	_SC_THREAD_PROCESS_SHARED        :: 66
+	_SC_THREAD_SAFE_FUNCTIONS        :: 67
+	_SC_TTY_NAME_MAX                 :: 68
+	_SC_HOST_NAME_MAX                :: 69
+
+	_SC_PASS_MAX                     :: 70
+	_SC_REGEXP                       :: 71
+	_SC_SHELL                        :: 72
+	_SC_SYMLOOP_MAX                  :: 73
+	_SC_V6_ILP32_OFF32               :: 74
+	_SC_V6_ILP32_OFFBIG              :: 75
+	_SC_V6_LP64_OFF64                :: 76
+	_SC_V6_LPBIG_OFFBIG              :: 77
+
+	_SC_2_PBS                        :: 80
+	_SC_2_PBS_ACCOUNTING             :: 81
+	_SC_2_PBS_CHECKPOINT             :: 82
+	_SC_2_PBS_LOCATE                 :: 83
+	_SC_2_PBS_MESSAGE                :: 84
+	_SC_2_PBS_TRACK                  :: 85
+	_SC_SPAWN                        :: 86
+	_SC_SHARED_MEMORY_OBJECTS        :: 87
+	_SC_TIMER_MAX                    :: 88
+	_SC_SEM_NSEMS_MAX                :: 89
+
+	_SC_CPUTIME                      :: 90
+	_SC_THREAD_CPUTIME               :: 91
+	_SC_DELAYTIMER_MAX               :: 92
+	_SC_SIGQUEUE_MAX                 :: 93
+	_SC_REALTIME_SIGNALS             :: 94
+	_SC_RTSIG_MAX                    :: 95
+
+	_POSIX_VDISABLE :: '\377'
+
+	// NOTE: following are not defined in netbsd headers.
+
+	_SC_SPORADIC_SERVER              :: 81
+	_SC_SEM_VALUE_MAX                :: 50
+
+	_SC_TRACE                        :: 97
+	_SC_TRACE_EVENT_FILTER           :: 98
+	_SC_TRACE_INHERIT                :: 99
+	_SC_TRACE_LOG                    :: 100
+	_SC_TYPED_MEMORY_OBJECTS         :: 102
+
+	_SC_THREAD_SPORADIC_SERVER       :: 92
+	_SC_TIMEOUTS                     :: 95
+
+	_SC_XOPEN_CRYPT                  :: 108
+	_SC_XOPEN_ENH_I18N               :: 109
+	_SC_XOPEN_REALTIME               :: 111
+	_SC_XOPEN_REALTIME_THREADS       :: 112
+	_SC_XOPEN_STREAMS                :: 114
+	_SC_XOPEN_UNIX                   :: 115
+	_SC_XOPEN_VERSION                :: 116
+	_SC_IPV6                         :: 118
+	_SC_RAW_SOCKETS                  :: 119
+
+	_SC_PRIORITIZED_IO               :: 34
+	_SC_AIO_PRIO_DELTA_MAX           :: 44
+	_SC_ADVISORY_INFO                :: 65
+	_SC_SS_REPL_MAX                  :: 126
+	_SC_TRACE_EVENT_NAME_MAX         :: 127
+	_SC_TRACE_NAME_MAX               :: 128
+	_SC_TRACE_SYS_MAX                :: 129
+	_SC_TRACE_USER_EVENT_MAX         :: 130
+
+} else when ODIN_OS == .OpenBSD {
+
+	_F_OK :: 0
+	X_OK :: 0x01
+	W_OK :: 0x02
+	R_OK :: 0x04
+
+	F_LOCK  :: 1
+	F_TEST  :: 3
+	F_TLOCK :: 2
+	F_ULOCK :: 0
+
+	_CS_PATH                            :: 1
+	_CS_POSIX_V6_ILP32_OFF32_CFLAGS		:: 2
+	_CS_POSIX_V6_ILP32_OFF32_LDFLAGS	:: 3
+	_CS_POSIX_V6_ILP32_OFF32_LIBS		:: 4
+	_CS_POSIX_V6_ILP32_OFFBIG_CFLAGS	:: 5
+	_CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS	:: 6
+	_CS_POSIX_V6_ILP32_OFFBIG_LIBS		:: 7
+	_CS_POSIX_V6_LP64_OFF64_CFLAGS		:: 8
+	_CS_POSIX_V6_LP64_OFF64_LDFLAGS		:: 9
+	_CS_POSIX_V6_LP64_OFF64_LIBS		:: 10
+	_CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS	:: 11
+	_CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS	:: 12
+	_CS_POSIX_V6_LPBIG_OFFBIG_LIBS		:: 13
+	_CS_POSIX_V6_WIDTH_RESTRICTED_ENVS	:: 14
+
+	_PC_LINK_MAX           :: 1
+	_PC_MAX_CANON          :: 2
+	_PC_MAX_INPUT          :: 3
+	_PC_NAME_MAX           :: 4
+	_PC_PATH_MAX           :: 5
+	_PC_PIPE_BUF           :: 6
+	_PC_CHOWN_RESTRICTED   :: 7
+	_PC_NO_TRUNC           :: 8
+	_PC_VDISABLE           :: 9
+	_PC_2_SYMLINK          :: 10
+	_PC_ALLOC_SIZE_MIN     :: 11
+	_PC_ASYNC_IO           :: 12
+	_PC_FILESIZEBITS       :: 13
+	_PC_PRIO_IO            :: 14
+	_PC_REC_INCR_XFER_SIZE :: 15
+	_PC_REC_MAX_XFER_SIZE  :: 16
+	_PC_REC_MIN_XFER_SIZE  :: 17
+	_PC_REC_XFER_ALIGN     :: 18
+	_PC_SYMLINK_MAX        :: 19
+	_PC_SYNC_IO            :: 20
+
+	_SC_ARG_MAX                      :: 1
+	_SC_CHILD_MAX                    :: 2
+	_SC_CLK_TCK                      :: 3
+	_SC_NGROUPS_MAX                  :: 4
+	_SC_OPEN_MAX                     :: 5
+	_SC_JOB_CONTROL                  :: 6
+	_SC_SAVED_IDS                    :: 7
+	_SC_VERSION                      :: 8
+	_SC_BC_BASE_MAX                  :: 9
+
+	_SC_BC_DIM_MAX                   :: 10
+	_SC_BC_SCALE_MAX                 :: 11
+	_SC_BC_STRING_MAX                :: 12
+	_SC_COLL_WEIGHTS_MAX             :: 13
+	_SC_EXPR_NEST_MAX                :: 14
+	_SC_LINE_MAX                     :: 15
+	_SC_RE_DUP_MAX                   :: 16
+	_SC_2_VERSION                    :: 17
+	_SC_2_C_BIND                     :: 18
+	_SC_2_C_DEV                      :: 19
+
+	_SC_2_CHAR_TERM                  :: 20
+	_SC_2_FORT_DEV                   :: 21
+	_SC_2_FORT_RUN                   :: 22
+	_SC_2_LOCALEDEF                  :: 23
+	_SC_2_SW_DEV                     :: 24
+	_SC_2_UPE                        :: 25
+	_SC_STREAM_MAX                   :: 26
+	_SC_TZNAME_MAX                   :: 27
+	_SC_PAGESIZE                     :: 28
+	_SC_PAGE_SIZE                    :: _SC_PAGESIZE
+	_SC_FSYNC                        :: 29
+
+	_SC_XOPEN_SHM                    :: 30
+	_SC_SEM_NSEMS_MAX                :: 31
+	_SC_SEM_VALUE_MAX                :: 32
+	_SC_HOST_NAME_MAX                :: 33
+	_SC_MONOTONIC_CLOCK              :: 34
+	_SC_2_PBS                        :: 35
+	_SC_2_PBS_ACCOUNTING             :: 36
+	_SC_2_PBS_CHECKPOINT             :: 37
+	_SC_2_PBS_LOCATE                 :: 38
+	_SC_2_PBS_MESSAGE                :: 39
+
+	_SC_2_PBS_TRACK                  :: 40
+	_SC_ADVISORY_INFO                :: 41
+	_SC_AIO_LISTIO_MAX               :: 42
+	_SC_AIO_MAX                      :: 43
+	_SC_AIO_PRIO_DELTA_MAX           :: 44
+	_SC_ASYNCHRONOUS_IO              :: 45
+	_SC_ATEXIT_MAX                   :: 46
+	_SC_BARRIERS                     :: 47
+	_SC_CLOCK_SELECTION              :: 48
+	_SC_CPUTIME                      :: 49
+
+	_SC_DELAYTIMER_MAX               :: 50
+	_SC_IOV_MAX                      :: 51
+	_SC_IPV6                         :: 52
+	_SC_MAPPED_FILES                 :: 53
+	_SC_MEMLOCK                      :: 54
+	_SC_MEMLOCK_RANGE                :: 55
+	_SC_MEMORY_PROTECTION            :: 56
+	_SC_MESSAGE_PASSING              :: 57
+	_SC_MQ_OPEN_MAX                  :: 58
+	_SC_MQ_PRIO_MAX                  :: 59
+
+	_SC_PRIORITIZED_IO               :: 60
+	_SC_PRIORITY_SCHEDULING          :: 61
+	_SC_RAW_SOCKETS                  :: 62
+	_SC_READER_WRITER_LOCKS          :: 63
+	_SC_REALTIME_SIGNALS             :: 64
+	_SC_REGEXP                       :: 65
+	_SC_RTSIG_MAX                    :: 66
+	_SC_SEMAPHORES                   :: 67
+	_SC_SHARED_MEMORY_OBJECTS        :: 68
+	_SC_SHELL                        :: 69
+
+	_SC_SIGQUEUE_MAX                 :: 70
+	_SC_SPAWN                        :: 71
+	_SC_SPIN_LOCKS                   :: 72
+	_SC_SPORADIC_SERVER              :: 73
+	_SC_SS_REPL_MAX                  :: 74
+	_SC_SYNCHRONIZED_IO              :: 75
+	_SC_SYMLOOP_MAX                  :: 76
+	_SC_THREAD_ATTR_STACKADDR        :: 77
+	_SC_THREAD_ATTR_STACKSIZE        :: 78
+	_SC_THREAD_CPUTIME               :: 79
+
+	_SC_THREAD_DESTRUCTOR_ITERATIONS :: 80
+	_SC_THREAD_KEYS_MAX              :: 81
+	_SC_THREAD_PRIO_INHERIT          :: 82
+	_SC_THREAD_PRIO_PROTECT          :: 83
+	_SC_THREAD_PRIORITY_SCHEDULING   :: 84
+	_SC_THREAD_PROCESS_SHARED        :: 85
+	_SC_THREAD_ROBUST_PRIO_INHERIT   :: 86
+	_SC_THREAD_ROBUST_PRIO_PROTECT   :: 87
+	_SC_THREAD_SPORADIC_SERVER       :: 88
+	_SC_THREAD_STACK_MIN             :: 89
+
+	_SC_THREAD_THREADS_MAX           :: 90
+	_SC_THREADS                      :: 91
+	_SC_TIMEOUTS                     :: 92
+	_SC_TIMER_MAX                    :: 93
+	_SC_TIMERS                       :: 94
+	_SC_TRACE                        :: 95
+	_SC_TRACE_EVENT_FILTER           :: 96
+	_SC_TRACE_EVENT_NAME_MAX         :: 97
+	_SC_TRACE_INHERIT                :: 98
+	_SC_TRACE_LOG                    :: 99
+
+	_SC_GETGR_R_SIZE_MAX             :: 100
+	_SC_GETPW_R_SIZE_MAX             :: 101
+	_SC_LOGIN_NAME_MAX               :: 102
+	_SC_THREAD_SAFE_FUNCTIONS        :: 103
+	_SC_TRACE_NAME_MAX               :: 104
+	_SC_TRACE_SYS_MAX                :: 105
+	_SC_TRACE_USER_EVENT_MAX         :: 106
+	_SC_TTY_NAME_MAX                 :: 107
+	_SC_TYPED_MEMORY_OBJECTS         :: 108
+	_SC_V6_ILP32_OFF32               :: 109
+
+	_SC_V6_ILP32_OFFBIG              :: 110
+	_SC_V6_LP64_OFF64                :: 111
+	_SC_V6_LPBIG_OFFBIG              :: 112
+	_SC_V7_ILP32_OFF32               :: 113
+	_SC_V7_ILP32_OFFBIG              :: 114
+	_SC_V7_LP64_OFF64                :: 115
+	_SC_V7_LPBIG_OFFBIG              :: 116
+	_SC_XOPEN_CRYPT                  :: 117
+	_SC_XOPEN_ENH_I18N               :: 118
+	_SC_XOPEN_LEGACY                 :: 119
+
+	_SC_XOPEN_REALTIME               :: 120
+	_SC_XOPEN_REALTIME_THREADS       :: 121
+	_SC_XOPEN_STREAMS                :: 122
+	_SC_XOPEN_UNIX                   :: 123
+	_SC_XOPEN_UUCP                   :: 124
+	_SC_XOPEN_VERSION                :: 125
+
+	_SC_PHYS_PAGES                   :: 500
+	_SC_AVPHYS_PAGES                 :: 501
+	_SC_NPROCESSORS_CONF             :: 502
+	_SC_NPROCESSORS_ONLN             :: 503
+
+	_POSIX_VDISABLE :: '\377'
+
+} else {
+	#panic("posix is unimplemented for the current target")
+}
+

+ 36 - 0
core/sys/posix/utime.odin

@@ -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 - 0
core/sys/posix/wordexp.odin

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

+ 1 - 1
core/sys/unix/pthread_netbsd.odin

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

+ 5 - 0
core/sys/unix/pthread_unix.odin

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

+ 0 - 83
core/sys/unix/time_unix.odin

@@ -1,83 +0,0 @@
-//+build linux, darwin, freebsd, openbsd, netbsd, haiku
-package unix
-
-when ODIN_OS == .Darwin {
-	foreign import libc "system:System.framework"
-} else  {
-	foreign import libc "system:c"
-}
-
-import "core:c"
-
-when ODIN_OS == .NetBSD {
-	@(default_calling_convention="c")
-		foreign libc {
-			@(link_name="__clock_gettime50") clock_gettime :: proc(clock_id: u64, timespec: ^timespec) -> c.int ---
-			@(link_name="__nanosleep50")     nanosleep     :: proc(requested, remaining: ^timespec) -> c.int ---
-			@(link_name="sleep")             sleep         :: proc(seconds: c.uint) -> c.int ---
-	}
-} else {
-	@(default_calling_convention="c")
-	foreign libc {
-		clock_gettime :: proc(clock_id: u64, timespec: ^timespec) -> c.int ---
-		sleep         :: proc(seconds: c.uint) -> c.int ---
-		nanosleep     :: proc(requested, remaining: ^timespec) -> c.int ---
-	}
-}
-
-timespec :: struct {
-	tv_sec:  i64, // seconds
-	tv_nsec: i64, // nanoseconds
-}
-
-when ODIN_OS == .OpenBSD {
-	CLOCK_REALTIME           :: 0
-	CLOCK_PROCESS_CPUTIME_ID :: 2
-	CLOCK_MONOTONIC          :: 3
-	CLOCK_THREAD_CPUTIME_ID  :: 4
-	CLOCK_UPTIME             :: 5
-	CLOCK_BOOTTIME           :: 6
-
-	// CLOCK_MONOTONIC_RAW doesn't exist, use CLOCK_MONOTONIC
-	CLOCK_MONOTONIC_RAW :: CLOCK_MONOTONIC
-} else {
-	CLOCK_REALTIME           :: 0 // NOTE(tetra): May jump in time, when user changes the system time.
-	CLOCK_MONOTONIC          :: 1 // NOTE(tetra): May stand still while system is asleep.
-	CLOCK_PROCESS_CPUTIME_ID :: 2
-	CLOCK_THREAD_CPUTIME_ID  :: 3
-	CLOCK_MONOTONIC_RAW      :: 4 // NOTE(tetra): "RAW" means: Not adjusted by NTP.
-	CLOCK_REALTIME_COARSE    :: 5 // NOTE(tetra): "COARSE" clocks are apparently much faster, but not "fine-grained."
-	CLOCK_MONOTONIC_COARSE   :: 6
-	CLOCK_BOOTTIME           :: 7 // NOTE(tetra): Same as MONOTONIC, except also including time system was asleep.
-	CLOCK_REALTIME_ALARM     :: 8
-	CLOCK_BOOTTIME_ALARM     :: 9
-}
-
-// TODO(tetra, 2019-11-05): The original implementation of this package for Darwin used this constants.
-// I do not know if Darwin programmers are used to the existance of these constants or not, so
-// I'm leaving aliases to them for now.
-CLOCK_SYSTEM   :: CLOCK_REALTIME
-CLOCK_CALENDAR :: CLOCK_MONOTONIC
-
-boot_time_in_nanoseconds :: proc "c" () -> i64 {
-	ts_now, ts_boottime: timespec
-	clock_gettime(CLOCK_REALTIME, &ts_now)
-	clock_gettime(CLOCK_BOOTTIME, &ts_boottime)
-
-	ns := (ts_now.tv_sec - ts_boottime.tv_sec) * 1e9 + ts_now.tv_nsec - ts_boottime.tv_nsec
-	return i64(ns)
-}
-
-seconds_since_boot :: proc "c" () -> f64 {
-	ts_boottime: timespec
-	clock_gettime(CLOCK_BOOTTIME, &ts_boottime)
-	return f64(ts_boottime.tv_sec) + f64(ts_boottime.tv_nsec) / 1e9
-}
-
-inline_nanosleep :: proc "c" (nanoseconds: i64) -> (remaining: timespec, res: i32) {
-	s, ns := nanoseconds / 1e9, nanoseconds % 1e9
-	requested := timespec{tv_sec=s, tv_nsec=ns}
-	res = nanosleep(&requested, &remaining)
-	return
-}
-

+ 38 - 0
core/time/time_linux.odin

@@ -0,0 +1,38 @@
+package time
+
+import "core:sys/linux"
+
+_IS_SUPPORTED :: true
+
+_now :: proc "contextless" () -> Time {
+	time_spec_now, _ := linux.clock_gettime(.REALTIME)
+	ns := time_spec_now.time_sec * 1e9 + time_spec_now.time_nsec
+	return Time{_nsec=i64(ns)}
+}
+
+_sleep :: proc "contextless" (d: Duration) {
+	ds := duration_seconds(d)
+	seconds := uint(ds)
+	nanoseconds := uint((ds - f64(seconds)) * 1e9)
+
+	ts := linux.Time_Spec{
+		time_sec  = seconds,
+		time_nsec = nanoseconds,
+	}
+
+	for {
+		if linux.nanosleep(&ts, &ts) != .EINTR {
+			break
+		}
+	}
+}
+
+_tick_now :: proc "contextless" () -> Tick {
+	t, _ := linux.clock_gettime(.MONOTONIC_RAW)
+	return Tick{_nsec = i64(t.time_sec*1e9 + t.time_nsec)}
+}
+
+_yield :: proc "contextless" () {
+	linux.sched_yield()
+}
+

+ 29 - 13
core/time/time_unix.odin

@@ -1,34 +1,50 @@
 //+private
-//+build linux, darwin, freebsd, openbsd, netbsd, haiku
+//+build darwin, freebsd, openbsd, netbsd, haiku
 package time
 
-import "core:sys/unix"
+import "core:sys/posix"
 
-_IS_SUPPORTED :: true // NOTE: Times on Darwin are UTC.
+_IS_SUPPORTED :: true
 
 _now :: proc "contextless" () -> Time {
-	time_spec_now: unix.timespec
-	unix.clock_gettime(unix.CLOCK_REALTIME, &time_spec_now)
-	ns := time_spec_now.tv_sec * 1e9 + time_spec_now.tv_nsec
+	time_spec_now: posix.timespec
+	posix.clock_gettime(.REALTIME, &time_spec_now)
+	ns := i64(time_spec_now.tv_sec) * 1e9 + time_spec_now.tv_nsec
 	return Time{_nsec=ns}
 }
 
 _sleep :: proc "contextless" (d: Duration) {
 	ds := duration_seconds(d)
-	seconds := u32(ds)
+	seconds := posix.time_t(ds)
 	nanoseconds := i64((ds - f64(seconds)) * 1e9)
 
-	if seconds > 0     { unix.sleep(seconds)   }
-	if nanoseconds > 0 { unix.inline_nanosleep(nanoseconds) }
+	ts := posix.timespec{
+		tv_sec  = seconds,
+		tv_nsec = nanoseconds,
+	}
+
+	for {
+		res := posix.nanosleep(&ts, &ts)
+		if res == .OK || posix.errno() != .EINTR {
+			break
+		}
+	}
+}
+
+when ODIN_OS == .Darwin {
+	TICK_CLOCK :: posix.Clock(4) // CLOCK_MONOTONIC_RAW
+} else {
+	// It looks like the BSDs don't have a CLOCK_MONOTONIC_RAW equivalent.
+	TICK_CLOCK :: posix.Clock.MONOTONIC
 }
 
 _tick_now :: proc "contextless" () -> Tick {
-	t: unix.timespec
-	unix.clock_gettime(unix.CLOCK_MONOTONIC_RAW, &t)
-	return Tick{_nsec = t.tv_sec*1e9 + t.tv_nsec}
+	t: posix.timespec
+	posix.clock_gettime(TICK_CLOCK, &t)
+	return Tick{_nsec = i64(t.tv_sec)*1e9 + t.tv_nsec}
 }
 
 _yield :: proc "contextless" () {
-	unix.sched_yield()
+	posix.sched_yield()
 }
 

+ 6 - 0
examples/all/all_posix.odin

@@ -0,0 +1,6 @@
+//+build darwin, openbsd, freebsd, netbsd
+package all
+
+import posix "core:sys/posix"
+
+_ :: posix

+ 4 - 0
src/big_int.cpp

@@ -621,3 +621,7 @@ gb_internal String big_int_to_string(gbAllocator allocator, BigInt const *x, u64
 	}
 	return make_string(cast(u8 *)buf.data, buf.count);
 }
+
+gb_internal int big_int_log2(BigInt const *x) {
+	return mp_count_bits(x) - 1;
+}

+ 17 - 0
src/check_builtin.cpp

@@ -3979,6 +3979,23 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
 		break;
 	}
 
+	case BuiltinProc_constant_log2: {
+		Operand o = {};
+		check_expr(c, &o, ce->args[0]);
+
+		if (!is_type_integer(o.type) && (o.mode != Addressing_Constant)) {
+			error(ce->args[0], "Expected a constant integer for '%.*s'", LIT(builtin_name));
+			return false;
+		}
+
+		int log2 = big_int_log2(&o.value.value_integer);
+
+		operand->mode = Addressing_Constant;
+		operand->value = exact_value_i64(cast(i64)log2);
+		operand->type = t_untyped_integer;
+		break;
+	}
+
 	case BuiltinProc_soa_struct: {
 		Operand x = {};
 		Operand y = {};

+ 18 - 2
src/check_decl.cpp

@@ -756,13 +756,29 @@ gb_internal bool are_signatures_similar_enough(Type *a_, Type *b_) {
 	for (isize i = 0; i < a->param_count; i++) {
 		Type *x = core_type(a->params->Tuple.variables[i]->type);
 		Type *y = core_type(b->params->Tuple.variables[i]->type);
+
+		if (x->kind == Type_BitSet && x->BitSet.underlying) {
+			x = core_type(x->BitSet.underlying);
+		}
+		if (y->kind == Type_BitSet && y->BitSet.underlying) {
+			y = core_type(y->BitSet.underlying);
+		}
+
 		if (!signature_parameter_similar_enough(x, y)) {
 			return false;
 		}
 	}
 	for (isize i = 0; i < a->result_count; i++) {
-		Type *x = base_type(a->results->Tuple.variables[i]->type);
-		Type *y = base_type(b->results->Tuple.variables[i]->type);
+		Type *x = core_type(a->results->Tuple.variables[i]->type);
+		Type *y = core_type(b->results->Tuple.variables[i]->type);
+
+		if (x->kind == Type_BitSet && x->BitSet.underlying) {
+			x = core_type(x->BitSet.underlying);
+		}
+		if (y->kind == Type_BitSet && y->BitSet.underlying) {
+			y = core_type(y->BitSet.underlying);
+		}
+
 		if (!signature_parameter_similar_enough(x, y)) {
 			return false;
 		}

+ 4 - 0
src/checker_builtin_procs.hpp

@@ -46,6 +46,8 @@ enum BuiltinProcId {
 
 	BuiltinProc_has_target_feature,
 
+	BuiltinProc_constant_log2,
+
 	BuiltinProc_transpose,
 	BuiltinProc_outer_product,
 	BuiltinProc_hadamard_product,
@@ -380,6 +382,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
 
 	{STR_LIT("has_target_feature"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 
+	{STR_LIT("constant_log2"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+
 	{STR_LIT("transpose"),        1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("outer_product"),    2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
 	{STR_LIT("hadamard_product"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},

+ 1 - 0
tests/core/normal.odin

@@ -38,6 +38,7 @@ download_assets :: proc() {
 @(require) import "slice"
 @(require) import "strconv"
 @(require) import "strings"
+@(require) import "sys/posix"
 @(require) import "sys/windows"
 @(require) import "text/i18n"
 @(require) import "text/match"

+ 211 - 0
tests/core/sys/posix/posix.odin

@@ -0,0 +1,211 @@
+//+build darwin, freebsd, openbsd, netbsd
+package tests_core_posix
+
+import "core:sys/posix"
+import "core:testing"
+import "core:log"
+import "core:strings"
+import "core:path/filepath"
+
+@(test)
+test_arpa_inet :: proc(t: ^testing.T) {
+
+	check :: proc(t: ^testing.T, $af: posix.AF, src: cstring, expect: posix.pton_result, loc := #caller_location) {
+		when af == .INET {
+			addr: posix.in_addr
+			dst: [posix.INET_ADDRSTRLEN]byte
+		} else {
+			addr: posix.in6_addr
+			dst: [posix.INET6_ADDRSTRLEN]byte
+		}
+
+		res := posix.inet_pton(af, src, &addr, size_of(addr))
+		testing.expect_value(t, res, expect, loc)
+
+		if expect == .SUCCESS {
+			back := posix.inet_ntop(af, &addr, raw_data(dst[:]), len(dst))
+			testing.expect_value(t, back, src, loc)
+
+			when af == .INET {
+				back = posix.inet_ntoa(addr)
+				testing.expect_value(t, back, src, loc)
+			}
+		}
+	}
+
+	check(t, .INET,  "127.0.0.1", .SUCCESS)
+	check(t, .INET,  "blah",      .INVALID)
+	check(t, .INET6, "::1",       .SUCCESS)
+	check(t, .INET6, "L",         .INVALID)
+	check(t, .UNIX,  "127.0.0.1", .AFNOSUPPORT)
+}
+
+@(test)
+test_dirent :: proc(t: ^testing.T) {
+	test := #load_directory(#directory)
+	test_map: map[string]struct{}
+	defer delete(test_map)
+
+	test_map[".."] = {}
+	test_map["."]  = {}
+
+	for file in test {
+		test_map[filepath.base(file.name)] = {}
+	}
+
+	{
+		list: [^]^posix.dirent
+		ret := posix.scandir(#directory, &list)
+		testing.expectf(t, ret >= 0, "%v >= 0: %v", ret, posix.strerror(posix.errno()))
+		defer posix.free(list)
+
+		entries := list[:ret]
+		for entry in entries {
+			defer posix.free(entry)
+
+			if entry.d_type != .REG {
+				continue
+			}
+
+			name := string(cstring(raw_data(entry.d_name[:])))
+			testing.expectf(t, name in test_map, "%v in %v", name, test_map)
+		}
+	}
+
+	{
+		dir := posix.opendir(#directory)
+		defer posix.closedir(dir)
+
+		for {
+			posix.set_errno(.NONE)
+			entry := posix.readdir(dir)
+			if entry == nil {
+				testing.expect_value(t, posix.errno(), posix.Errno.NONE)
+				break
+			}
+
+			if entry.d_type != .REG {
+				continue
+			}
+
+			name := string(cstring(raw_data(entry.d_name[:])))
+			testing.expectf(t, name in test_map, "%v in %v", name, test_map)
+		}
+	}
+}
+
+@(test)
+test_errno :: proc(t: ^testing.T) {
+	posix.errno(posix.Errno.ENOMEM)
+	testing.expect_value(t, posix.errno(), posix.Errno.ENOMEM)
+
+	res := posix.open("", {})
+	testing.expect_value(t, res, -1)
+	testing.expect_value(t, posix.errno(), posix.Errno.ENOENT)
+}
+
+@(test)
+test_fcntl :: proc(t: ^testing.T) {
+	res := posix.open(#file, { .WRONLY, .CREAT, .EXCL })
+	testing.expect_value(t, res, -1)
+	testing.expect_value(t, posix.errno(), posix.Errno.EEXIST)
+}
+
+@(test)
+test_fnmatch :: proc(t: ^testing.T) {
+	testing.expect_value(t, posix.fnmatch("*.odin", #file, {}), 0)
+	testing.expect_value(t, posix.fnmatch("*.txt", #file, {}), posix.FNM_NOMATCH)
+	testing.expect_value(t, posix.fnmatch("**/*.odin", #file, {}), 0)
+}
+
+@(test)
+test_glob :: proc(t: ^testing.T) {
+	glob: posix.glob_t
+	res := posix.glob(#directory + ":)))))))", {}, nil, &glob)
+	testing.expect_value(t, res, posix.Glob_Result.NOMATCH)
+	posix.globfree(&glob)
+}
+
+@(test)
+test_langinfo :: proc(t: ^testing.T) {
+	locale := posix.setlocale(.TIME, nil)
+	testing.expectf(t, locale == "POSIX" || locale == "C", "invalid locale for test: %v", locale)
+
+	day1 := posix.nl_langinfo(.DAY_1)
+	testing.expect_value(t, day1, "Sunday")
+}
+
+@(test)
+test_libgen :: proc(t: ^testing.T) {
+	tests := [][3]cstring{
+		{ "usr",              ".",          "usr" },
+		{ "usr/",             ".",          "usr" },
+		{ "",                 ".",          "." },
+		{ "/",                "/",          "/" },
+		{ "//",               "/",          "/" },
+		{ "///",              "/",          "/" },
+		{ "/usr/",            "/",          "usr" },
+		{ "/usr/lib",         "/usr",       "lib" },
+		{ "//usr//lib//",     "//usr",      "lib" },
+		{ "/home//dwc//test", "/home//dwc", "test" },
+	}
+
+	for test in tests {
+		// NOTE: dir/basename can change their input so they can't be literals.
+
+		dinput := strings.clone_to_cstring(string(test[0]))
+		defer delete(dinput)
+
+		dir := posix.dirname(dinput)
+		testing.expectf(t, dir == test[1], "dirname(%q) == %q, expected %q", test[0], dir, test[1])
+
+		binput := strings.clone_to_cstring(string(test[0]))
+		defer delete(binput)
+
+		base := posix.basename(binput)
+		testing.expectf(t, base == test[2], "basename(%q) == %q, expected %q", test[0], base, test[2])
+	}
+}
+
+@(test)
+test_locale :: proc(t: ^testing.T) {
+	lconv := posix.localeconv()
+	testing.expect(t, lconv != nil)
+
+	locale := posix.setlocale(.ALL, nil)
+	testing.expectf(t, locale == "POSIX" || locale == "C", "%q is not POSIX or C", locale)
+}
+
+@(test)
+test_monetary :: proc(t: ^testing.T) {
+	when ODIN_OS == .Darwin && .Address in ODIN_SANITIZER_FLAGS {
+		log.warn("skipping on darwin with -sanitize:address, this fails inside macOS (also from C/clang)")
+		return
+	}
+
+	value := 123456.789
+	buf: [128]byte
+	size := posix.strfmon(raw_data(buf[:]), len(buf), "%n", value)
+	testing.expectf(t, int(size) != -1, "strfmon failure: %v", posix.strerror(posix.errno()))
+	log.debug(string(buf[:size]))
+}
+
+@(test)
+test_stat :: proc(t: ^testing.T) {
+	testing.expect_value(t, posix.S_IRWXU, transmute(posix.mode_t)posix._mode_t(posix._S_IRWXU))
+	testing.expect_value(t, posix.S_IRWXG, transmute(posix.mode_t)posix._mode_t(posix._S_IRWXG))
+	testing.expect_value(t, posix.S_IRWXO, transmute(posix.mode_t)posix._mode_t(posix._S_IRWXO))
+	testing.expect_value(t, posix._S_IFMT, transmute(posix.mode_t)posix._mode_t(posix.__S_IFMT))
+}
+
+@(test)
+test_termios :: proc(t: ^testing.T) {
+	testing.expect_value(t, transmute(posix.CControl_Flags)posix.tcflag_t(posix._CSIZE),  posix.CSIZE)
+
+	testing.expect_value(t, transmute(posix.COutput_Flags)posix.tcflag_t(posix._NLDLY),  posix.NLDLY)
+	testing.expect_value(t, transmute(posix.COutput_Flags)posix.tcflag_t(posix._CRDLY),  posix.CRDLY)
+	testing.expect_value(t, transmute(posix.COutput_Flags)posix.tcflag_t(posix._TABDLY), posix.TABDLY)
+	testing.expect_value(t, transmute(posix.COutput_Flags)posix.tcflag_t(posix._BSDLY),  posix.BSDLY)
+	testing.expect_value(t, transmute(posix.COutput_Flags)posix.tcflag_t(posix._VTDLY),  posix.VTDLY)
+	testing.expect_value(t, transmute(posix.COutput_Flags)posix.tcflag_t(posix._FFDLY),  posix.FFDLY)
+}

+ 127 - 0
tests/core/sys/posix/structs.odin

@@ -0,0 +1,127 @@
+//+build darwin, freebsd, openbsd, netbsd
+package tests_core_posix
+
+import "core:log"
+import "core:testing"
+import "core:sys/posix"
+
+// This test tests some of the process APIs of posix while also double checking size and alignment
+// of the structs we bound.
+
+@(test)
+execute_struct_checks :: proc(t: ^testing.T) {
+	log.debug("compiling C project")
+	{
+		switch pid := posix.fork(); pid {
+		case -1:
+			log.errorf("fork() failed: %s", posix.strerror())
+		case 0:
+			c_compiler := posix.getenv("CC")
+			if c_compiler == nil {
+				c_compiler = "clang"
+			}
+
+			posix.execlp(c_compiler,
+				c_compiler, #directory + "/structs/structs.c", "-o", #directory + "/structs/c_structs", nil)
+			posix.exit(69)
+		case:
+			if !wait_for(t, pid) { return }
+			log.debug("C code has been compiled!")
+		}
+	}
+
+	log.debug("compiling Odin project")
+	{
+		switch pid := posix.fork(); pid {
+		case -1:
+			log.errorf("fork() failed: %s", posix.strerror())
+		case 0:
+			posix.execlp(ODIN_ROOT + "/odin",
+				ODIN_ROOT + "/odin", "build", #directory + "/structs/structs.odin", "-out:" + #directory + "/structs/odin_structs", "-file", nil)
+			posix.exit(69)
+		case:
+			if !wait_for(t, pid) { return }
+			log.debug("Odin code has been compiled!")
+		}
+	}
+
+	c_buf: [dynamic]byte
+	defer delete(c_buf)
+	c_out := get_output(t, &c_buf, #directory + "/structs/c_structs", nil)
+
+	odin_buf: [dynamic]byte
+	defer delete(odin_buf)
+	odin_out := get_output(t, &odin_buf, #directory + "/structs/odin_structs", nil)
+
+	testing.expectf(t, c_out == odin_out, "The C output and Odin output differ!\nC output:\n%s\n\n\n\nOdin Output:\n%s", c_out, odin_out)
+
+	/* ----------- HELPERS ----------- */
+
+	wait_for :: proc(t: ^testing.T, pid: posix.pid_t) -> (ok: bool) {
+		log.debugf("waiting on pid %v", pid)
+
+		waiting: for {
+			status: i32
+			wpid := posix.waitpid(pid, &status, {})
+			if !testing.expectf(t, wpid != -1, "waitpid() failure: %v", posix.strerror()) {
+				return false
+			}
+
+			switch {
+			case posix.WIFEXITED(status):
+				ok = testing.expect_value(t, posix.WEXITSTATUS(status), 0)
+				break waiting
+			case posix.WIFSIGNALED(status):
+				log.errorf("child process raised: %v", posix.strsignal(posix.WTERMSIG(status)))
+				ok = false
+				break waiting
+			case:
+				log.errorf("unexpected status (this should never happen): %v", status)
+				ok = false
+				break waiting
+			}
+		}
+
+		return
+	}
+
+	get_output :: proc(t: ^testing.T, output: ^[dynamic]byte, cmd: ..cstring) -> (out_str: string) {
+		log.debugf("capturing output of: %v", cmd)
+
+		pipe: [2]posix.FD
+		if !testing.expect_value(t, posix.pipe(&pipe), posix.result.OK) {
+			return
+		}
+
+		switch pid := posix.fork(); pid {
+		case -1:
+			log.errorf("fork() failed: %s", posix.strerror())
+			return
+		case 0:
+			posix.close(pipe[0])
+			posix.dup2(pipe[1], 1)
+			posix.execv(cmd[0], raw_data(cmd[:]))
+			panic(string(posix.strerror()))
+		case:
+			posix.close(pipe[1])
+			log.debugf("waiting on pid %v", pid)
+
+			reader: for {
+				buf: [256]byte
+				switch read := posix.read(pipe[0], &buf[0], 256); {
+				case read  < 0:
+					log.errorf("read output failed: %v", posix.strerror())
+					return
+				case read == 0:
+					break reader
+				case:
+					append(output, ..buf[:read])
+				}
+			}
+
+			wait_for(t, pid)
+
+			return string(output[:])
+		}
+	}
+}

+ 2 - 0
tests/core/sys/posix/structs/.gitignore

@@ -0,0 +1,2 @@
+c_structs
+odin_structs

+ 103 - 0
tests/core/sys/posix/structs/structs.c

@@ -0,0 +1,103 @@
+#include <dirent.h>
+#include <fcntl.h>
+#include <glob.h>
+#include <grp.h>
+#include <locale.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <termios.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <poll.h>
+#include <pwd.h>
+#include <sys/shm.h>
+#include <sys/ipc.h>
+#include <sys/msg.h>
+#include <sys/un.h>
+#include <stddef.h>
+#include <sys/resource.h>
+#include <sys/utsname.h>
+#include <sys/times.h>
+#include <signal.h>
+#include <sys/select.h>
+#include <sys/uio.h>
+#include <sys/sem.h>
+#include <sys/statvfs.h>
+#include <sys/time.h>
+#include <utime.h>
+#include <wordexp.h>
+#include <sys/socket.h>
+
+int main(int argc, char *argv[])
+{
+    printf("dirent %zu %zu\n", sizeof(struct dirent), _Alignof(struct dirent));
+    printf("flock %zu %zu\n", sizeof(struct flock), _Alignof(struct flock));
+    printf("glob_t %zu %zu\n", sizeof(glob_t), _Alignof(glob_t));
+	printf("group %zu %zu\n", sizeof(struct group), _Alignof(struct group));
+    printf("lconv %zu %zu\n", sizeof(struct lconv), _Alignof(struct lconv));
+
+    printf("pthread_t %zu %zu\n", sizeof(pthread_t), _Alignof(pthread_t));
+    printf("pthread_attr_t %zu %zu\n", sizeof(pthread_attr_t), _Alignof(pthread_attr_t));
+    printf("pthread_key_t %zu %zu\n", sizeof(pthread_key_t), _Alignof(pthread_key_t));
+
+    printf("sched_param %zu %zu\n", sizeof(struct sched_param), _Alignof(struct sched_param));
+
+	printf("termios %zu %zu\n", sizeof(struct termios), _Alignof(struct termios));
+
+	printf("in_addr %zu %zu\n", sizeof(struct in_addr), _Alignof(struct in_addr));
+	printf("in6_addr %zu %zu\n", sizeof(struct in6_addr), _Alignof(struct in6_addr));
+	printf("sockaddr_in %zu %zu\n", sizeof(struct sockaddr_in), _Alignof(struct sockaddr_in));
+	printf("sockaddr_in6 %zu %zu\n", sizeof(struct sockaddr_in6), _Alignof(struct sockaddr_in6));
+	printf("ipv6_mreq %zu %zu\n", sizeof(struct ipv6_mreq), _Alignof(struct ipv6_mreq));
+
+	printf("sockaddr_storage %zu %zu\n", sizeof(struct sockaddr_storage), _Alignof(struct sockaddr_storage));
+	printf("msghdr %zu %zu\n", sizeof(struct msghdr), _Alignof(struct msghdr));
+	printf("cmsghdr %zu %zu\n", sizeof(struct cmsghdr), _Alignof(struct cmsghdr));
+	printf("linger %zu %zu\n", sizeof(struct linger), _Alignof(struct linger));
+
+	printf("hostent %zu %zu\n", sizeof(struct hostent), _Alignof(struct hostent));
+	printf("netent %zu %zu\n", sizeof(struct netent), _Alignof(struct netent));
+	printf("protoent %zu %zu\n", sizeof(struct protoent), _Alignof(struct protoent));
+	printf("servent %zu %zu\n", sizeof(struct servent), _Alignof(struct servent));
+	printf("addrinfo %zu %zu\n", sizeof(struct addrinfo), _Alignof(struct addrinfo));
+
+	printf("pollfd %zu %zu\n", sizeof(struct pollfd), _Alignof(struct pollfd));
+
+	printf("passwd %zu %zu\n", sizeof(struct passwd), _Alignof(struct passwd));
+
+	printf("shmid_ds %zu %zu\n", sizeof(struct shmid_ds), _Alignof(struct shmid_ds));
+	printf("ipc_perm %zu %zu\n", sizeof(struct ipc_perm), _Alignof(struct ipc_perm));
+	printf("msqid_ds %zu %zu\n", sizeof(struct msqid_ds), _Alignof(struct msqid_ds));
+
+	printf("rlimit %zu %zu\n", sizeof(struct rlimit), _Alignof(struct rlimit));
+	printf("rusage %zu %zu\n", sizeof(struct rusage), _Alignof(struct rusage));
+
+	printf("sockaddr_un %zu %zu\n", sizeof(struct sockaddr_un), _Alignof(struct sockaddr_un));
+
+	printf("utsname %zu %zu\n", sizeof(struct utsname), _Alignof(struct utsname));
+
+	printf("tms %zu %zu\n", sizeof(struct tms), _Alignof(struct tms));
+
+	printf("sigaction %zu %zu\n", sizeof(struct sigaction), _Alignof(struct sigaction));
+	printf("stack_t %zu %zu\n", sizeof(stack_t), _Alignof(stack_t));
+	printf("siginfo_t %zu %zu\n", sizeof(siginfo_t), _Alignof(siginfo_t));
+
+	printf("fd_set %zu %zu\n", sizeof(fd_set), _Alignof(fd_set));
+
+	printf("iovec %zu %zu\n", sizeof(struct iovec), _Alignof(struct iovec));
+
+	printf("semid_ds %zu %zu\n", sizeof(struct semid_ds), _Alignof(struct semid_ds));
+	printf("sembuf %zu %zu\n", sizeof(struct sembuf), _Alignof(struct sembuf));
+
+	printf("itimerval %zu %zu\n", sizeof(struct itimerval), _Alignof(struct itimerval));
+
+	printf("utimbuf %zu %zu\n", sizeof(struct utimbuf), _Alignof(struct utimbuf));
+
+	printf("wordexp_t %zu %zu\n", sizeof(wordexp_t), _Alignof(wordexp_t));
+
+	printf("time_t %zu %zu\n", sizeof(time_t), _Alignof(time_t));
+	printf("timespec %zu %zu\n", sizeof(struct timespec), _Alignof(struct timespec));
+	printf("clock_t %zu %zu\n", sizeof(clock_t), _Alignof(clock_t));
+
+	return 0;
+}

+ 74 - 0
tests/core/sys/posix/structs/structs.odin

@@ -0,0 +1,74 @@
+package main
+
+import "core:fmt"
+import "core:sys/posix"
+
+main :: proc() {
+	fmt.println("dirent", size_of(posix.dirent), align_of(posix.dirent))
+	fmt.println("flock", size_of(posix.flock), align_of(posix.flock))
+	fmt.println("glob_t", size_of(posix.glob_t), align_of(posix.glob_t))
+	fmt.println("group", size_of(posix.group), align_of(posix.group))
+	fmt.println("lconv", size_of(posix.lconv), align_of(posix.lconv))
+
+	fmt.println("pthread_t", size_of(posix.pthread_t), align_of(posix.pthread_t))
+	fmt.println("pthread_attr_t", size_of(posix.pthread_attr_t), align_of(posix.pthread_attr_t))
+	fmt.println("pthread_key_t", size_of(posix.pthread_key_t), align_of(posix.pthread_key_t))
+
+	fmt.println("sched_param", size_of(posix.sched_param), align_of(posix.sched_param))
+
+	fmt.println("termios", size_of(posix.termios), align_of(posix.termios))
+	
+	fmt.println("in_addr", size_of(posix.in_addr), align_of(posix.in_addr))
+	fmt.println("in6_addr", size_of(posix.in6_addr), align_of(posix.in6_addr))
+	fmt.println("sockaddr_in", size_of(posix.sockaddr_in), align_of(posix.sockaddr_in))
+	fmt.println("sockaddr_in6", size_of(posix.sockaddr_in6), align_of(posix.sockaddr_in6))
+	fmt.println("ipv6_mreq", size_of(posix.ipv6_mreq), align_of(posix.ipv6_mreq))
+
+	fmt.println("sockaddr_storage", size_of(posix.sockaddr_storage), align_of(posix.sockaddr_storage))
+	fmt.println("msghdr", size_of(posix.msghdr), align_of(posix.msghdr))
+	fmt.println("cmsghdr", size_of(posix.cmsghdr), align_of(posix.cmsghdr))
+	fmt.println("linger", size_of(posix.linger), align_of(posix.linger))
+
+	fmt.println("hostent", size_of(posix.hostent), align_of(posix.hostent))
+	fmt.println("netent", size_of(posix.netent), align_of(posix.netent))
+	fmt.println("protoent", size_of(posix.protoent), align_of(posix.protoent))
+	fmt.println("servent", size_of(posix.servent), align_of(posix.servent))
+	fmt.println("addrinfo", size_of(posix.addrinfo), align_of(posix.addrinfo))
+
+	fmt.println("pollfd", size_of(posix.pollfd), align_of(posix.pollfd))
+	fmt.println("passwd", size_of(posix.passwd), align_of(posix.passwd))
+
+	fmt.println("shmid_ds", size_of(posix.shmid_ds), align_of(posix.shmid_ds))
+	fmt.println("ipc_perm", size_of(posix.ipc_perm), align_of(posix.ipc_perm))
+	fmt.println("msqid_ds", size_of(posix.msqid_ds), align_of(posix.msqid_ds))
+
+	fmt.println("rlimit", size_of(posix.rlimit), align_of(posix.rlimit))
+	fmt.println("rusage", size_of(posix.rusage), align_of(posix.rusage))
+
+	fmt.println("sockaddr_un", size_of(posix.sockaddr_un), align_of(posix.sockaddr_un))
+
+	fmt.println("utsname", size_of(posix.utsname), align_of(posix.utsname))
+
+	fmt.println("tms", size_of(posix.tms), align_of(posix.tms))
+
+	fmt.println("sigaction", size_of(posix.sigaction_t), align_of(posix.sigaction_t))
+	fmt.println("stack_t", size_of(posix.stack_t), align_of(posix.stack_t))
+	fmt.println("siginfo_t", size_of(posix.siginfo_t), align_of(posix.siginfo_t))
+
+	fmt.println("fd_set", size_of(posix.fd_set), align_of(posix.fd_set))
+
+	fmt.println("iovec", size_of(posix.iovec), align_of(posix.iovec))
+
+	fmt.println("semid_ds", size_of(posix.semid_ds), align_of(posix.semid_ds))
+	fmt.println("sembuf", size_of(posix.sembuf), align_of(posix.sembuf))
+
+	fmt.println("itimerval", size_of(posix.itimerval), align_of(posix.itimerval))
+
+	fmt.println("utimbuf", size_of(posix.utimbuf), align_of(posix.utimbuf))
+
+	fmt.println("wordexp_t", size_of(posix.wordexp_t), align_of(posix.wordexp_t))
+
+	fmt.println("time_t", size_of(posix.time_t), align_of(posix.time_t))
+	fmt.println("timespec", size_of(posix.timespec), align_of(posix.timespec))
+	fmt.println("clock_t", size_of(posix.clock_t), align_of(posix.clock_t))
+}