Pārlūkot izejas kodu

sys/posix: impl rest of linux, impl some of Windows

Laytan Laats 9 mēneši atpakaļ
vecāks
revīzija
1cebc025b0
63 mainītis faili ar 1237 papildinājumiem un 789 dzēšanām
  1. 2 2
      core/c/libc/README.md
  2. 133 0
      core/c/libc/locale.odin
  3. 1 1
      core/os/os_linux.odin
  4. 1 0
      core/sys/posix/arpa_inet.odin
  5. 1 2
      core/sys/posix/dirent.odin
  6. 1 2
      core/sys/posix/dlfcn.odin
  7. 80 2
      core/sys/posix/errno.odin
  8. 3 4
      core/sys/posix/fcntl.odin
  9. 1 2
      core/sys/posix/fnmatch.odin
  10. 1 2
      core/sys/posix/glob.odin
  11. 1 2
      core/sys/posix/grp.odin
  12. 1 0
      core/sys/posix/iconv.odin
  13. 1 0
      core/sys/posix/langinfo.odin
  14. 8 0
      core/sys/posix/libgen.odin
  15. 1 2
      core/sys/posix/limits.odin
  16. 6 126
      core/sys/posix/locale.odin
  17. 1 0
      core/sys/posix/monetary.odin
  18. 1 2
      core/sys/posix/net_if.odin
  19. 1 2
      core/sys/posix/netdb.odin
  20. 6 2
      core/sys/posix/netinet_in.odin
  21. 1 2
      core/sys/posix/netinet_tcp.odin
  22. 1 3
      core/sys/posix/poll.odin
  23. 3 0
      core/sys/posix/posix.odin
  24. 2 3
      core/sys/posix/pthread.odin
  25. 13 2
      core/sys/posix/pwd.odin
  26. 1 2
      core/sys/posix/sched.odin
  27. 1 5
      core/sys/posix/setjmp.odin
  28. 11 0
      core/sys/posix/setjmp_libc.odin
  29. 2 109
      core/sys/posix/signal.odin
  30. 145 0
      core/sys/posix/signal_libc.odin
  31. 8 165
      core/sys/posix/stdio.odin
  32. 207 0
      core/sys/posix/stdio_libc.odin
  33. 1 78
      core/sys/posix/stdlib.odin
  34. 101 0
      core/sys/posix/stdlib_libc.odin
  35. 1 14
      core/sys/posix/string.odin
  36. 30 0
      core/sys/posix/string_libc.odin
  37. 1 2
      core/sys/posix/sys_ipc.odin
  38. 1 2
      core/sys/posix/sys_mman.odin
  39. 21 2
      core/sys/posix/sys_msg.odin
  40. 1 2
      core/sys/posix/sys_resource.odin
  41. 2 3
      core/sys/posix/sys_select.odin
  42. 1 2
      core/sys/posix/sys_sem.odin
  43. 21 2
      core/sys/posix/sys_shm.odin
  44. 5 5
      core/sys/posix/sys_socket.odin
  45. 120 67
      core/sys/posix/sys_stat.odin
  46. 24 2
      core/sys/posix/sys_statvfs.odin
  47. 1 2
      core/sys/posix/sys_time.odin
  48. 1 2
      core/sys/posix/sys_times.odin
  49. 1 2
      core/sys/posix/sys_uio.odin
  50. 1 2
      core/sys/posix/sys_un.odin
  51. 17 10
      core/sys/posix/sys_utsname.odin
  52. 51 2
      core/sys/posix/sys_wait.odin
  53. 8 9
      core/sys/posix/termios.odin
  54. 13 2
      core/sys/posix/time.odin
  55. 1 2
      core/sys/posix/ulimit.odin
  56. 2 110
      core/sys/posix/unistd.odin
  57. 153 0
      core/sys/posix/unistd_libc.odin
  58. 1 2
      core/sys/posix/utime.odin
  59. 1 2
      core/sys/posix/wordexp.odin
  60. 2 15
      tests/core/sys/posix/posix.odin
  61. 1 1
      tests/core/sys/posix/structs.odin
  62. 2 0
      tests/core/sys/posix/structs/structs.c
  63. 5 1
      tests/core/sys/posix/structs/structs.odin

+ 2 - 2
core/c/libc/README.md

@@ -14,7 +14,7 @@ The following is a mostly-complete projection of the C11 standard library as def
 | `<inttypes.h>`    | Fully projected                                    |
 | `<iso646.h>`      | Not applicable, use Odin's operators               |
 | `<limits.h>`      | Not projected                                      |
-| `<locale.h>`      | Not projected                                      |
+| `<locale.h>`      | Fully projected                                    |
 | `<math.h>`        | Mostly projected, see [limitations](#Limitations)  |
 | `<setjmp.h>`      | Fully projected                                    |
 | `<signal.h>`      | Fully projected                                    |
@@ -70,4 +70,4 @@ with the following copyright.
 
 ```
 Copyright 2021 Dale Weiler <[email protected]>.
-```
+```

+ 133 - 0
core/c/libc/locale.odin

@@ -0,0 +1,133 @@
+package libc
+
+import "core:c"
+
+when ODIN_OS == .Windows {
+	foreign import libc "system:libucrt.lib"
+} else when ODIN_OS == .Darwin {
+	foreign import libc "system:System.framework"
+} else {
+	foreign import libc "system:c"
+}
+
+// locale.h - category macros
+
+foreign libc {
+	/*
+	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 == .Windows {
+	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,
+		_W_decimal_point:     [^]u16 `fmt:"s,0"`,
+		_W_thousands_sep:     [^]u16 `fmt:"s,0"`,
+		_W_int_curr_symbol:   [^]u16 `fmt:"s,0"`,
+		_W_currency_symbol:   [^]u16 `fmt:"s,0"`,
+		_W_mon_decimal_point: [^]u16 `fmt:"s,0"`,
+		_W_mon_thousands_sep: [^]u16 `fmt:"s,0"`,
+		_W_positive_sign:     [^]u16 `fmt:"s,0"`,
+		_W_negative_sign:     [^]u16 `fmt:"s,0"`,
+	}
+} else {
+	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,
+	}
+}
+
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD  || ODIN_OS == .OpenBSD || ODIN_OS == .Windows {
+
+	LC_ALL      :: 0
+	LC_COLLATE  :: 1
+	LC_CTYPE    :: 2
+	LC_MESSAGES :: 6
+	LC_MONETARY :: 3
+	LC_NUMERIC  :: 4
+	LC_TIME     :: 5
+
+} else when ODIN_OS == .Linux {
+
+	LC_CTYPE    :: 0
+	LC_NUMERIC  :: 1
+	LC_TIME     :: 2
+	LC_COLLATE  :: 3
+	LC_MONETARY :: 4
+	LC_MESSAGES :: 5
+	LC_ALL      :: 6
+
+}

+ 1 - 1
core/os/os_linux.odin

@@ -490,7 +490,7 @@ foreign libc {
 	@(link_name="free")             _unix_free          :: proc(ptr: rawptr) ---
 	@(link_name="realloc")          _unix_realloc       :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---
 
-	@(link_name="execvp")           _unix_execvp       :: proc(path: cstring, argv: [^]cstring) -> int ---
+	@(link_name="execvp")           _unix_execvp       :: proc(path: cstring, argv: [^]cstring) -> c.int ---
 	@(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 ---

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

@@ -1,3 +1,4 @@
+#+build darwin, linux, freebsd, openbsd, netbsd
 package posix
 
 import "core:c"

+ 1 - 2
core/sys/posix/dirent.odin

@@ -1,3 +1,4 @@
+#+build darwin, linux, freebsd, openbsd, netbsd
 package posix
 
 import "core:c"
@@ -210,6 +211,4 @@ when ODIN_OS == .Darwin {
 			d_name:   [256]c.char `fmt:"s,0"`, /* [PSX] entry name */
 		}
 
-} else {
-	#panic("posix is unimplemented for the current target")
 }

+ 1 - 2
core/sys/posix/dlfcn.odin

@@ -1,3 +1,4 @@
+#+build darwin, linux, freebsd, openbsd, netbsd
 package posix
 
 import "core:c"
@@ -120,7 +121,5 @@ when ODIN_OS == .Darwin {
 	_RTLD_LOCAL  :: 0
 	RTLD_LOCAL   :: RTLD_Flags{}
 
-} else {
-	#panic("posix is unimplemented for the current target")
 }
 

+ 80 - 2
core/sys/posix/errno.odin

@@ -1,3 +1,4 @@
+#+build windows, darwin, linux, freebsd, openbsd, netbsd
 package posix
 
 import "core:c"
@@ -456,7 +457,84 @@ when ODIN_OS == .Darwin {
 
 	EOWNERDEAD      :: 130
 	ENOTRECOVERABLE :: 131
-} else {
-	#panic("posix is unimplemented for the current target")
+} else when ODIN_OS == .Windows {
+	E2BIG           :: 7
+	EACCES          :: 13
+	EADDRINUSE      :: 100
+	EADDRNOTAVAIL   :: 101
+	EAFNOSUPPORT    :: 102
+	EAGAIN          :: 11
+	EALREADY        :: 103
+	EBADF           :: 9
+	EBADMSG         :: 104
+	EBUSY           :: 16
+	ECANCELED       :: 105
+	ECHILD          :: 10
+	ECONNABORTED    :: 106
+	ECONNREFUSED    :: 107
+	ECONNRESET      :: 108
+	EDEADLK         :: 36
+	EDESTADDRREQ    :: 109
+	EDQUOT          :: -1 // NOTE: not defined
+	EEXIST          :: 17
+	EFAULT          :: 14
+	EFBIG           :: 27
+	EHOSTUNREACH    :: 110
+	EIDRM           :: 111
+	EINPROGRESS     :: 112
+	EINTR           :: 4
+	EINVAL          :: 22
+	EIO             :: 5
+	EISCONN         :: 113
+	EISDIR          :: 21
+	ELOOP           :: 114
+	EMFILE          :: 24
+	EMLINK          :: 31
+	EMSGSIZE        :: 115
+	EMULTIHOP       :: -1 // NOTE: not defined
+	ENAMETOOLONG    :: 38
+	ENETDOWN        :: 116
+	ENETRESET       :: 117
+	ENETUNREACH     :: 118
+	ENFILE          :: 23
+	ENOBUFS         :: 119
+	ENODATA         :: 120
+	ENODEV          :: 19
+	ENOENT          :: 2
+	ENOEXEC         :: 8
+	ENOLCK          :: 39
+	ENOLINK         :: 121
+	ENOMEM          :: 12
+	ENOMSG          :: 122
+	ENOPROTOOPT     :: 123
+	ENOSPC          :: 28
+	ENOSR           :: 124
+	ENOSTR          :: 125
+	ENOSYS          :: 40
+	ENOTCONN        :: 126
+	ENOTDIR         :: 20
+	ENOTEMPTY       :: 41
+	ENOTRECOVERABLE :: 127
+	ENOTSOCK        :: 128
+	ENOTSUP         :: 129
+	ENOTTY          :: 25
+	ENXIO           :: 6
+	EOPNOTSUPP      :: 130
+	EOVERFLOW       :: 132
+	EOWNERDEAD      :: 133
+	EPERM           :: 1
+	EPIPE           :: 32
+	EPROTO          :: 134
+	EPROTONOSUPPORT :: 135
+	EPROTOTYPE      :: 136
+	EROFS           :: 30
+	ESPIPE          :: 29
+	ESRCH           :: 3
+	ESTALE          :: -1 // NOTE: not defined
+	ETIME           :: 137
+	ETIMEDOUT       :: 138
+	ETXTBSY         :: 139
+	EWOULDBLOCK     :: 140
+	EXDEV           :: 18
 }
 

+ 3 - 4
core/sys/posix/fcntl.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, openbsd, freebsd, netbsd
 package posix
 
 import "core:c"
@@ -466,13 +467,11 @@ when ODIN_OS == .Darwin {
 	AT_REMOVEDIR        :: 0x200
 
 	flock :: struct {
+		l_type:   Lock_Type, /* [PSX] type of lock. */
+		l_whence: c.short,   /* [PSX] flag (Whence) of starting offset. */
 		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")
 }

+ 1 - 2
core/sys/posix/fnmatch.odin

@@ -1,3 +1,4 @@
+#+build darwin, linux, openbsd, freebsd, netbsd
 package posix
 
 import "core:c"
@@ -61,6 +62,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 	FNM_NOESCAPE :: 0x02
 	FNM_PERIOD   :: 0x04
 
-} else {
-	#panic("posix is unimplemented for the current target")
 }

+ 1 - 2
core/sys/posix/glob.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "core:c"
@@ -203,6 +204,4 @@ when ODIN_OS == .Darwin {
 	GLOB_ABORTED :: 2
 	GLOB_NOMATCH :: 3
 
-} else {
-	#panic("posix is unimplemented for the current target")
 }

+ 1 - 2
core/sys/posix/grp.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "core:c"
@@ -125,6 +126,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 		gr_mem:    [^]cstring, /* [PSX] group members */
 	}
 
-} else {
-	#panic("posix is unimplemented for the current target")
 }

+ 1 - 0
core/sys/posix/iconv.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "core:c"

+ 1 - 0
core/sys/posix/langinfo.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "core:c"

+ 8 - 0
core/sys/posix/libgen.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 when ODIN_OS == .Darwin {
@@ -56,6 +57,7 @@ foreign lib {
 
 	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/basename.html ]]
 	*/
+	@(link_name=LBASENAME)
 	basename :: proc(path: cstring) -> cstring ---
 
 	/*
@@ -72,3 +74,9 @@ foreign lib {
 	*/
 	dirname :: proc(path: cstring) -> cstring ---
 }
+
+when ODIN_OS == .Linux {
+	@(private) LBASENAME :: "__xpg_basename"
+} else {
+	@(private) LBASENAME :: "basename"
+}

+ 1 - 2
core/sys/posix/limits.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 // limits.h - implementation-defined constants
@@ -549,6 +550,4 @@ when ODIN_OS == .Darwin {
 	NL_TEXTMAX :: 2048 // 255 on glibc, 2048 on musl
 	NZERO      :: 20
 
-} else {
-	#panic("posix is unimplemented for the current target")
 }

+ 6 - 126
core/sys/posix/locale.odin

@@ -1,131 +1,11 @@
+#+build windows, linux, darwin, netbsd, openbsd, freebsd
 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"
-}
+localeconv      :: libc.localeconv
+setlocale       :: libc.setlocale
 
-// locale.h - category macros
+lconv           :: libc.lconv
 
-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 when ODIN_OS == .Linux {
-
-	// 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_CTYPE    :: 0
-	LC_NUMERIC  :: 1
-	LC_TIME     :: 2
-	LC_COLLATE  :: 3
-	LC_MONETARY :: 4
-	LC_MESSAGES :: 5
-	LC_ALL      :: 6
-
-} else {
-	#panic("posix is unimplemented for the current target")
-}
+Locale_Category :: libc.Locale_Category

+ 1 - 0
core/sys/posix/monetary.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "core:c"

+ 1 - 2
core/sys/posix/net_if.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "core:c"
@@ -55,6 +56,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 
 	IF_NAMESIZE :: 16
 
-} else {
-	#panic("posix is unimplemented for the current target")
 }

+ 1 - 2
core/sys/posix/netdb.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "core:c"
@@ -472,6 +473,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 		EAI_OVERFLOW :: 14
 	}
 
-} else {
-	#panic("posix is unimplemented for the current target")
 }

+ 6 - 2
core/sys/posix/netinet_in.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "core:c"
@@ -61,6 +62,11 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 			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 */
+		}
+
 		IPV6_MULTICAST_IF   :: 17
 		IPV6_UNICAST_HOPS   :: 16
 		IPV6_MULTICAST_HOPS :: 18
@@ -223,6 +229,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 		)
 	}
 
-} else {
-	#panic("posix is unimplemented for the current target")
 }

+ 1 - 2
core/sys/posix/netinet_tcp.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 // netinet/tcp.h - definitions for the Internet Transmission Control Protocol (TCP)
@@ -6,6 +7,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 
 	TCP_NODELAY :: 0x01
 
-} else {
-	#panic("posix is unimplemented for the current target")
 }

+ 1 - 3
core/sys/posix/poll.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "base:intrinsics"
@@ -92,7 +93,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 	POLLHUP    :: 0x0010
 	POLLNVAL   :: 0x0020
 
-} else {
-	#panic("posix is unimplemented for the current target")
 }
-

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

@@ -11,6 +11,9 @@ The struct fields that are cross-platform are documented with `[PSX]`.
 Accessing these fields on one target should be the same on others.
 Other fields are implementation specific.
 
+The parts of POSIX that Windows implements are also supported here, but
+other symbols are undefined on Windows targets.
+
 Most macros have been reimplemented in Odin with inlined functions.
 
 Unimplemented headers:

+ 2 - 3
core/sys/posix/pthread.odin

@@ -1,10 +1,11 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "core:c"
 
 when ODIN_OS == .Darwin {
 	foreign import lib "system:System.framework"
-} else when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD {
+} else when ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .Linux {
 	foreign import lib "system:pthread"
 } else {
 	foreign import lib "system:c"
@@ -557,6 +558,4 @@ when ODIN_OS == .Darwin {
 		__reserved3: c.int,
 	}
 
-} else {
-	#panic("posix is unimplemented for the current target")
 }

+ 13 - 2
core/sys/posix/pwd.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "core:c"
@@ -163,6 +164,16 @@ when ODIN_OS == .Darwin || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
 		pw_fields: c.int,
 	}
 
-} else {
-	#panic("posix is unimplemented for the current target")
+} else when ODIN_OS == .Linux {
+
+	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_gecos:  cstring, /* Real name.  */
+		pw_dir:    cstring, /* Home directory.  */
+		pw_shell:  cstring, /* Shell program.  */
+	}
+
 }

+ 1 - 2
core/sys/posix/sched.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "core:c"
@@ -100,6 +101,4 @@ when ODIN_OS == .Darwin {
 	SCHED_FIFO     :: 1
 	SCHED_RR       :: 2
 
-} else {
-	#panic("posix is unimplemented for the current target")
 }

+ 1 - 5
core/sys/posix/setjmp.odin

@@ -1,7 +1,7 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "core:c"
-import "core:c/libc"
 
 when ODIN_OS == .Darwin {
 	foreign import lib "system:System.framework"
@@ -43,12 +43,8 @@ foreign lib {
 	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"

+ 11 - 0
core/sys/posix/setjmp_libc.odin

@@ -0,0 +1,11 @@
+#+build windows, linux, darwin, netbsd, openbsd, freebsd
+package posix
+
+import "core:c/libc"
+
+// setjmp.h - stack environment declarations
+
+jmp_buf :: libc.jmp_buf
+
+longjmp :: libc.longjmp
+setjmp  :: libc.setjmp

+ 2 - 109
core/sys/posix/signal.odin

@@ -1,9 +1,9 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "base:intrinsics"
 
 import "core:c"
-import "core:c/libc"
 
 when ODIN_OS == .Darwin {
 	foreign import lib "system:System.framework"
@@ -14,31 +14,6 @@ when ODIN_OS == .Darwin {
 // 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.
 
@@ -227,72 +202,6 @@ sigval :: struct #raw_union {
 	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,
@@ -434,20 +343,6 @@ Sig :: enum c.int {
 	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"
@@ -1118,7 +1013,7 @@ when ODIN_OS == .Darwin {
 
 	uid_t :: distinct c.uint32_t
 	sigset_t :: struct {
-		[1024/(8 * size_of(c.ulong))]val,
+		__val: [1024/(8 * size_of(c.ulong))]c.ulong,
 	}
 
 	SIGHUP    :: 1
@@ -1285,6 +1180,4 @@ when ODIN_OS == .Darwin {
 	SI_TIMER   :: -2
 	SI_MESGQ   :: -3
 	SI_ASYNCIO :: -4
-} else {
-	#panic("posix is unimplemented for the current target")
 }

+ 145 - 0
core/sys/posix/signal_libc.odin

@@ -0,0 +1,145 @@
+#+build linux, windows, darwin, netbsd, openbsd, freebsd
+package posix
+
+import "base:intrinsics"
+
+import "core:c"
+import "core:c/libc"
+
+when ODIN_OS == .Windows {
+	foreign import lib "system:libucrt.lib"
+} else when ODIN_OS == .Darwin {
+	foreign import lib "system:System.framework"
+} else {
+	foreign import lib "system:c"
+}
+
+// signal.h - signals
+
+foreign lib {
+	/*
+	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 ---
+}
+
+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,
+}
+
+// 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 == .Windows {
+	SIGALRM   :: -1
+	SIGBUS    :: -1
+	SIGCHLD   :: -1
+	SIGCONT   :: -1
+	SIGHUP    :: -1
+	SIGKILL   :: -1
+	SIGPIPE   :: -1
+	SIGQUIT   :: -1
+	SIGSTOP   :: -1
+	SIGTSTP   :: -1
+	SIGTTIN   :: -1
+	SIGTTOU   :: -1
+	SIGUSR1   :: -1
+	SIGUSR2   :: -1
+	SIGPOLL   :: -1
+	SIGPROF   :: -1
+	SIGSYS    :: -1
+	SIGTRAP   :: -1
+	SIGURG    :: -1
+	SIGVTALRM :: -1
+	SIGXCPU   :: -1
+	SIGXFSZ   :: -1
+}

+ 8 - 165
core/sys/posix/stdio.odin

@@ -1,7 +1,7 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "core:c"
-import "core:c/libc"
 
 when ODIN_OS == .Darwin {
 	foreign import lib "system:System.framework"
@@ -32,16 +32,6 @@ foreign lib {
 	*/
 	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.
 
@@ -115,34 +105,6 @@ foreign lib {
 	*/
 	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.
 
@@ -181,60 +143,6 @@ foreign lib {
 	*/
 	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.
 
@@ -243,76 +151,6 @@ foreign lib {
 	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
@@ -327,6 +165,11 @@ when ODIN_OS == .Darwin {
 
 	P_tmpdir :: "/tmp/"
 
-} else {
-	#panic("posix is unimplemented for the current target")
+} else when ODIN_OS == .Linux {
+
+	L_ctermid :: 20 // 20 on musl, 9 on glibc
+	L_tmpnam  :: 20
+
+	P_tmpdir :: "/tmp/"
+
 }

+ 207 - 0
core/sys/posix/stdio_libc.odin

@@ -0,0 +1,207 @@
+#+build linux, windows, linux, darwin, netbsd, openbsd, freebsd
+package posix
+
+import "core:c"
+import "core:c/libc"
+
+when ODIN_OS == .Windows {
+	foreign import lib {
+		"system:libucrt.lib",
+		"system:legacy_stdio_definitions.lib",
+	}
+} else when ODIN_OS == .Darwin {
+	foreign import lib "system:System.framework"
+} else {
+	foreign import lib "system:c"
+}
+
+// stdio.h - standard buffered input/output
+
+when ODIN_OS == .Windows {
+	@(private) LGETC_UNLOCKED    :: "_getc_nolock"
+	@(private) LGETCHAR_UNLOCKED :: "_getchar_nolock"
+	@(private) LPUTC_UNLOCKED    :: "_putc_nolock"
+	@(private) LPUTCHAR_UNLOCKED :: "_putchar_nolock"
+	@(private) LTEMPNAM          :: "_tempnam"
+	@(private) LPOPEN            :: "_popen"
+	@(private) LPCLOSE           :: "_pclose"
+} else {
+	@(private) LGETC_UNLOCKED    :: "getc_unlocked"
+	@(private) LGETCHAR_UNLOCKED :: "getchar_unlocked"
+	@(private) LPUTC_UNLOCKED    :: "putc_unlocked"
+	@(private) LPUTCHAR_UNLOCKED :: "putchar_unlocked"
+	@(private) LTEMPNAM          :: "tempnam"
+	@(private) LPOPEN            :: "popen"
+	@(private) LPCLOSE           :: "pclose"
+}
+
+foreign lib {
+	/*
+	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 ---
+
+	/*
+	Equivalent to getc but unaffected by locks.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getc_unlocked.html ]]
+	*/
+	@(link_name=LGETC_UNLOCKED)
+	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 ]]
+	*/
+	@(link_name=LGETCHAR_UNLOCKED)
+	getchar_unlocked :: proc() -> c.int ---
+
+	/*
+	Equivalent to putc but unaffected by locks.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/getc_unlocked.html ]]
+	*/
+	@(link_name=LPUTC_UNLOCKED)
+	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 ]]
+	*/
+	@(link_name=LPUTCHAR_UNLOCKED)
+	putchar_unlocked :: proc(ch: c.int) -> c.int ---
+
+	/*
+	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 ]]
+	*/
+	@(link_name=LTEMPNAM)
+	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 ]]
+	*/
+	@(link_name=LPOPEN)
+	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 ]]	
+	*/
+	@(link_name=LPCLOSE)
+	pclose :: proc(stream: ^FILE) -> c.int ---
+}
+
+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

+ 1 - 78
core/sys/posix/stdlib.odin

@@ -1,9 +1,9 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "base:intrinsics"
 
 import "core:c"
-import "core:c/libc"
 
 when ODIN_OS == .Darwin {
 	foreign import lib "system:System.framework"
@@ -11,56 +11,6 @@ when ODIN_OS == .Darwin {
 	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,
@@ -342,21 +292,6 @@ foreign lib {
 	*/
 	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.
 
@@ -427,23 +362,11 @@ foreign lib {
 	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"

+ 101 - 0
core/sys/posix/stdlib_libc.odin

@@ -0,0 +1,101 @@
+#+build linux, windows, darwin, netbsd, openbsd, freebsd
+package posix
+
+import "base:intrinsics"
+
+import "core:c"
+import "core:c/libc"
+
+when ODIN_OS == .Windows {
+	foreign import lib "system:libucrt.lib"
+} else 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 {
+
+	/*
+	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 ---
+}
+
+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 == .Windows {
+	@(private) LPUTENV :: "_putenv"
+} else when ODIN_OS == .NetBSD {
+	@(private) LPUTENV :: "__putenv50"
+} else {
+	@(private) LPUTENV :: "putenv"
+}

+ 1 - 14
core/sys/posix/string.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "core:c"
@@ -13,16 +14,6 @@ when ODIN_OS == .Darwin {
 // 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.
 
@@ -41,7 +32,3 @@ foreign lib {
 	*/
 	strsignal :: proc(sig: Signal) -> cstring ---
 }
-
-strerror :: #force_inline proc "contextless" (errnum: Maybe(Errno) = nil) -> cstring {
-	return _strerror(errnum.? or_else errno())
-}

+ 30 - 0
core/sys/posix/string_libc.odin

@@ -0,0 +1,30 @@
+#+build linux, windows, darwin, netbsd, openbsd, freebsd
+package posix
+
+when ODIN_OS == .Windows {
+	foreign import lib "system:libucrt.lib"
+} else 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 ---
+}
+
+strerror :: #force_inline proc "contextless" (errnum: Maybe(Errno) = nil) -> cstring {
+	return _strerror(errnum.? or_else errno())
+}

+ 1 - 2
core/sys/posix/sys_ipc.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "core:c"
@@ -110,6 +111,4 @@ when ODIN_OS == .Darwin {
 	IPC_SET  :: 1
 	IPC_STAT :: 2
 
-} else {
-	#panic("posix is unimplemented for the current target")
 }

+ 1 - 2
core/sys/posix/sys_mman.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "core:c"
@@ -225,6 +226,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS
 	POSIX_MADV_SEQUENTIAL :: 2
 	POSIX_MADV_WILLNEED   :: 3
 
-} else {
-	#panic("posix is unimplemented for the current target")
 }

+ 21 - 2
core/sys/posix/sys_msg.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "core:c"
@@ -150,6 +151,24 @@ when ODIN_OS == .Darwin {
 		msg_pad4:    [4]c.long,
 	}
 
-} else {
-	#panic("posix is unimplemented for the current target")
+} else when ODIN_OS == .Linux {
+
+	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_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_cbytes:  c.ulong,
+		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() */
+		__unused:    [2]c.ulong,
+	}
+
 }

+ 1 - 2
core/sys/posix/sys_resource.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "core:c"
@@ -154,6 +155,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 		RLIMIT_AS :: 10
 	}
 
-} else {
-	#panic("posix is unimplemented for the current target")
 }

+ 2 - 3
core/sys/posix/sys_select.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "base:intrinsics"
@@ -72,7 +73,7 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 
 	// 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)
+	ALIGN ::  align_of(c.long) when ODIN_OS == .FreeBSD || ODIN_OS == .Linux 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,
@@ -115,6 +116,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 		intrinsics.mem_zero(_p, size_of(fd_set))
 	}
 
-} else {
-	#panic("posix is unimplemented for the current target")
 }

+ 1 - 2
core/sys/posix/sys_sem.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "core:c"
@@ -153,6 +154,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 		sem_flg: c.short,  /* [PSX] operation flags */
 	}
 
-} else {
-	#panic("posix is unimplemented for the current target")
 }

+ 21 - 2
core/sys/posix/sys_shm.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "core:c"
@@ -137,6 +138,24 @@ when ODIN_OS == .Darwin {
 		_shm_internal:   rawptr,
 	}
 
-} else {
-	#panic("posix is unimplemented for the current target")
+} else when ODIN_OS == .Linux {
+
+	SHM_RDONLY :: 0o10000
+	SHM_RND    :: 0o20000
+
+	SHMLBA     :: 4096
+
+	shmatt_t :: distinct c.ulong
+
+	shmid_ds :: struct {
+		shm_perm:        ipc_perm, /* [PSX] operation permission structure */
+		shm_segsz:       c.size_t, /* [PSX] size of segment in bytes */
+		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_cpid:        pid_t,    /* [PSX] process ID of creator */
+		shm_lpid:        pid_t,    /* [PSX] process ID of last shared memory operation */
+		shm_nattch:      shmatt_t, /* [PSX] number of current attaches */
+		_:               [2]c.ulong,
+	}
 }

+ 5 - 5
core/sys/posix/sys_socket.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "core:c"
@@ -325,14 +326,16 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 
 	socklen_t :: distinct c.uint
 
-	_sa_family_t :: distinct c.uint8_t
-
 	when ODIN_OS == .Linux {
+		_sa_family_t :: distinct c.ushort
+
 		sockaddr :: struct {
 			sa_family: sa_family_t, /* [PSX] address family */
 			sa_data:   [14]c.char,  /* [PSX] socket address */
 		}
 	} else {
+		_sa_family_t :: distinct c.uint8_t
+
 		sockaddr :: struct {
 			sa_len:    c.uint8_t,   /* total length */
 			sa_family: sa_family_t, /* [PSX] address family */
@@ -560,7 +563,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 	SHUT_RDWR :: 2
 	SHUT_WR   :: 1
 
-} else {
-	#panic("posix is unimplemented for the current target")
 }
-

+ 120 - 67
core/sys/posix/sys_stat.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "core:c"
@@ -280,20 +281,20 @@ when ODIN_OS == .Darwin {
 	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_dev:           dev_t,        /* [PSX] ID of device containing file */
+		st_mode:          mode_t,       /* [PSX] mode of file */
+		st_nlink:         nlink_t,      /* [PSX] number of hard links */
+		st_ino:           ino_t,        /* [PSX] file serial number */
+		st_uid:           uid_t,        /* [PSX] user ID of the file */
+		st_gid:           gid_t,        /* [PSX] group ID of the file */
+		st_rdev:          dev_t,        /* [PSX] device ID */
+		st_atim:          timespec,     /* [PSX] time of last access */
+		st_mtim:          timespec,     /* [PSX] time of last data modification */
+		st_ctim:          timespec,     /* [PSX] 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_size:          off_t,        /* [PSX] file size, in bytes */
+		st_blocks:        blkcnt_t,     /* [PSX] blocks allocated for file */
+		st_blksize:       blksize_t,    /* [PSX] 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 */
@@ -314,47 +315,47 @@ when ODIN_OS == .Darwin {
 
 	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_dev:           dev_t,        /* [PSX] ID of device containing file */
+			st_ino:           ino_t,        /* [PSX] file serial number */
+			st_nlink:         nlink_t,      /* [PSX] number of hard links */
+			st_mode:          mode_t,       /* [PSX] 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_uid:           uid_t,        /* [PSX] user ID of the file */
+			st_gid:           gid_t,        /* [PSX] group ID of the file */
 			st_padding1:      c.int32_t,
-			st_rdev:          dev_t,        /* [XSI] device ID */
+			st_rdev:          dev_t,        /* [PSX] device ID */
 			st_atim_ext:      c.int32_t,
-			st_atim:          timespec,     /* [XSI] time of last access */
+			st_atim:          timespec,     /* [PSX] time of last access */
 			st_mtim_ext:      c.int32_t,
-			st_mtim:          timespec,     /* [XSI] time of last data modification */
+			st_mtim:          timespec,     /* [PSX] time of last data modification */
 			st_ctim_ext:      c.int32_t,
-			st_ctim:          timespec,     /* [XSI] time of last status change */
+			st_ctim:          timespec,     /* [PSX] 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_size:          off_t,        /* [PSX] file size, in bytes */
+			st_blocks:        blkcnt_t,     /* [PSX] blocks allocated for file */
+			st_blksize:       blksize_t,    /* [PSX] 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_dev:           dev_t,        /* [PSX] ID of device containing file */
+			st_ino:           ino_t,        /* [PSX] file serial number */
+			st_nlink:         nlink_t,      /* [PSX] number of hard links */
+			st_mode:          mode_t,       /* [PSX] 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_uid:           uid_t,        /* [PSX] user ID of the file */
+			st_gid:           gid_t,        /* [PSX] 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_rdev:          dev_t,        /* [PSX] device ID */
+			st_atim:          timespec,     /* [PSX] time of last access */
+			st_mtim:          timespec,     /* [PSX] time of last data modification */
+			st_ctim:          timespec,     /* [PSX] 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_size:          off_t,        /* [PSX] file size, in bytes */
+			st_blocks:        blkcnt_t,     /* [PSX] blocks allocated for file */
+			st_blksize:       blksize_t,    /* [PSX] 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,
@@ -374,20 +375,20 @@ when ODIN_OS == .Darwin {
 	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_dev:           dev_t,        /* [PSX] ID of device containing file */
+		st_mode:          mode_t,       /* [PSX] mode of file */
+		st_ino:           ino_t,        /* [PSX] file serial number */
+		st_nlink:         nlink_t,      /* [PSX] number of hard links */
+		st_uid:           uid_t,        /* [PSX] user ID of the file */
+		st_gid:           gid_t,        /* [PSX] group ID of the file */
+		st_rdev:          dev_t,        /* [PSX] device ID */
+		st_atim:          timespec,     /* [PSX] time of last access */
+		st_mtim:          timespec,     /* [PSX] time of last data modification */
+		st_ctim:          timespec,     /* [PSX] 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_size:          off_t,        /* [PSX] file size, in bytes */
+		st_blocks:        blkcnt_t,     /* [PSX] blocks allocated for file */
+		st_blksize:       blksize_t,    /* [PSX] 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,
@@ -406,19 +407,19 @@ when ODIN_OS == .Darwin {
 	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_mode:          mode_t,       /* [PSX] mode of file */
+		st_dev:           dev_t,        /* [PSX] ID of device containing file */
+		st_ino:           ino_t,        /* [PSX] file serial number */
+		st_nlink:         nlink_t,      /* [PSX] number of hard links */
+		st_uid:           uid_t,        /* [PSX] user ID of the file */
+		st_gid:           gid_t,        /* [PSX] group ID of the file */
+		st_rdev:          dev_t,        /* [PSX] device ID */
+		st_atim:          timespec,     /* [PSX] time of last access */
+		st_mtim:          timespec,     /* [PSX] time of last data modification */
+		st_ctim:          timespec,     /* [PSX] time of last status change */
+		st_size:          off_t,        /* [PSX] file size, in bytes */
+		st_blocks:        blkcnt_t,     /* [PSX] blocks allocated for file */
+		st_blksize:       blksize_t,    /* [PSX] optimal blocksize for I/O */
 		st_flags:         c.uint32_t,   /* user defined flags for file */
 		st_gen:           c.int32_t,
 		st_birthtimespec: timespec,
@@ -427,6 +428,58 @@ when ODIN_OS == .Darwin {
 	UTIME_NOW  :: -2
 	UTIME_OMIT :: -1
 
-} else {
-	#panic("posix is unimplemented for the current target")
+} else when ODIN_OS == .Linux {
+
+	dev_t     :: distinct u64
+	_mode_t   :: distinct c.uint 
+	blkcnt_t  :: distinct i64
+
+	when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
+		nlink_t   :: distinct c.uint
+		blksize_t :: distinct c.int
+	} else {
+		nlink_t   :: distinct c.size_t
+		blksize_t :: distinct c.long
+	}
+
+	ino_t :: distinct u64
+
+	when ODIN_ARCH == .amd64 {
+		stat_t :: struct {
+			st_dev:           dev_t,        /* [PSX] ID of device containing file */
+			st_ino:           ino_t,        /* [PSX] file serial number */
+			st_nlink:         nlink_t,      /* [PSX] number of hard links */
+			st_mode:          mode_t,       /* [PSX] mode of file */
+			st_uid:           uid_t,        /* [PSX] user ID of the file */
+			st_gid:           gid_t,        /* [PSX] group ID of the file */
+			_pad0:            c.uint,
+			st_rdev:          dev_t,        /* [PSX] device ID */
+			st_size:          off_t,        /* [PSX] file size, in bytes */
+			st_blksize:       blksize_t,    /* [PSX] optimal blocksize for I/O */
+			st_blocks:        blkcnt_t,     /* [PSX] blocks allocated for file */
+			st_atim:          timespec,     /* [PSX] time of last access */
+			st_mtim:          timespec,     /* [PSX] time of last data modification */
+			st_ctim:          timespec,     /* [PSX] time of last status change */
+			__unused:         [3]c.long,
+		}
+	} else {
+		stat_t :: struct {
+			st_dev:           dev_t,        /* [PSX] ID of device containing file */
+			st_ino:           ino_t,        /* [PSX] file serial number */
+			st_mode:          mode_t,       /* [PSX] mode of file */
+			st_nlink:         nlink_t,      /* [PSX] number of hard links */
+			st_uid:           uid_t,        /* [PSX] user ID of the file */
+			st_gid:           gid_t,        /* [PSX] group ID of the file */
+			st_rdev:          dev_t,        /* [PSX] device ID */
+			__pad:            c.ulonglong,
+			st_size:          off_t,        /* [PSX] file size, in bytes */
+			st_blksize:       blksize_t,    /* [PSX] optimal blocksize for I/O */
+			__pad2:           c.int,
+			st_blocks:        blkcnt_t,     /* [PSX] blocks allocated for file */
+			st_atim:          timespec,     /* [PSX] time of last access */
+			st_mtim:          timespec,     /* [PSX] time of last data modification */
+			st_ctim:          timespec,     /* [PSX] time of last status change */
+			__unused:         [2]c.uint,
+		}
+	}
 }

+ 24 - 2
core/sys/posix/sys_statvfs.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "core:c"
@@ -130,6 +131,27 @@ when ODIN_OS == .Darwin || ODIN_OS == .OpenBSD {
 	ST_RDONLY :: 0x00000001
 	ST_NOSUID :: 0x00000008
 
-} else {
-	#panic("posix is unimplemented for the current target")
+} else when ODIN_OS == .Linux {
+
+	fsblkcnt_t :: distinct c.uint64_t
+
+	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 */
+		_:          [2*size_of(c.int)-size_of(c.long)]byte,
+		f_flag:     VFS_Flags,  /* [PSX] bit mask of f_flag values */
+		f_namemax:  c.ulong,    /* [PSX] maximum filename length */
+		f_type:     c.uint,
+		__reserved: [5]c.int,
+	}
+
+	ST_RDONLY :: 0x00000001
+	ST_NOSUID :: 0x00000002
 }

+ 1 - 2
core/sys/posix/sys_time.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "core:c"
@@ -77,6 +78,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 	ITIMER_VIRTUAL :: 1
 	ITIMER_PROF    :: 2
 
-} else {
-	#panic("posix is unimplemented for the current target")
 }

+ 1 - 2
core/sys/posix/sys_times.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 when ODIN_OS == .Darwin {
@@ -33,6 +34,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 		tms_cstime: clock_t, /* [PSX] terminated children system CPU time */
 	}
 
-} else {
-	#panic("posix is unimplemented for the current target")
 }

+ 1 - 2
core/sys/posix/sys_uio.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "core:c"
@@ -37,6 +38,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 		iov_len:  c.size_t, /* [PSX] size of the region iov_base points to */
 	}
 
-} else {
-	#panic("posix is unimplemented for the current target")
 }

+ 1 - 2
core/sys/posix/sys_un.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "core:c"
@@ -19,6 +20,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 		sun_path:   [108]c.char, /* [PSX] socket pathname */
 	}
 
-} else {
-	#panic("posix is unimplemented for the current target")
 }

+ 17 - 10
core/sys/posix/sys_utsname.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "core:c"
@@ -37,15 +38,10 @@ foreign lib {
 	uname :: proc(uname: ^utsname) -> c.int ---
 }
 
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
 
-	when ODIN_OS == .Linux {
-		@(private)
-		_SYS_NAMELEN :: 65
-	} else {
-		@(private)
-		_SYS_NAMELEN :: 256
-	}
+	@(private)
+	_SYS_NAMELEN :: 256
 
 	utsname :: struct {
 		sysname:  [_SYS_NAMELEN]c.char `fmt:"s,0"`, /* [PSX] name of OS */
@@ -55,6 +51,17 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 		machine:  [_SYS_NAMELEN]c.char `fmt:"s,0"`, /* [PSX] hardware type */
 	}
 
-} else {
-	#panic("posix is unimplemented for the current target")
+} else when ODIN_OS == .Linux {
+
+	@(private)
+	_SYS_NAMELEN :: 65
+
+	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 */
+		__domainname: [_SYS_NAMELEN]c.char `fmt:"s,0"`,
+	}
 }

+ 51 - 2
core/sys/posix/sys_wait.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "core:c"
@@ -391,6 +392,54 @@ when ODIN_OS == .Darwin {
 		return (x & _WCONTINUED) == _WCONTINUED
 	}
 
-} else {
-	#panic("posix is unimplemented for the current target")
+} else when ODIN_OS == .Linux {
+
+	id_t :: distinct c.uint
+
+	WCONTINUED :: 8
+	WNOHANG    :: 1
+	WUNTRACED  :: 2
+
+	WEXITED  :: 4
+	WNOWAIT  :: 0x1000000
+	WSTOPPED :: 2
+
+	_P_ALL  :: 0
+	_P_PID  :: 1
+	_P_PGID :: 2
+
+	@(private)
+	_WIFEXITED :: #force_inline proc "contextless" (x: c.int) -> bool {
+		return _WTERMSIG(x) == nil
+	}
+
+	@(private)
+	_WEXITSTATUS :: #force_inline proc "contextless" (x: c.int) -> c.int {
+		return (x & 0xff00) >> 8
+	}
+
+	@(private)
+	_WIFSIGNALED :: #force_inline proc "contextless" (x: c.int) -> bool {
+		return (x & 0xffff) - 1 < 0xff
+	}
+
+	@(private)
+	_WTERMSIG :: #force_inline proc "contextless" (x: c.int) -> Signal {
+		return Signal(x & 0x7f)
+	}
+
+	@(private)
+	_WIFSTOPPED :: #force_inline proc "contextless" (x: c.int) -> bool {
+		return ((x & 0xffff) * 0x10001) >> 8 > 0x7f00
+	}
+
+	@(private)
+	_WSTOPSIG :: #force_inline proc "contextless" (x: c.int) -> Signal {
+		return Signal(_WEXITSTATUS(x))
+	}
+
+	@(private)
+	_WIFCONTINUED :: #force_inline proc "contextless" (x: c.int) -> bool {
+		return x == 0xffff
+	}
 }

+ 8 - 9
core/sys/posix/termios.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "core:c"
@@ -152,7 +153,7 @@ CControl_Flag_Bits :: enum tcflag_t {
 CControl_Flags :: bit_set[CControl_Flag_Bits; tcflag_t]
 
 // character size mask
-CSIZE :: CControl_Flags{ .CS6, .CS7, .CS8 }
+CSIZE :: transmute(CControl_Flags)tcflag_t(_CSIZE)
 
 COutput_Flag_Bits :: enum tcflag_t {
 	OPOST  = log2(OPOST),  /* enable following output processing */
@@ -181,17 +182,17 @@ COutput_Flag_Bits :: enum tcflag_t {
 COutput_Flags :: bit_set[COutput_Flag_Bits; tcflag_t]
 
 // \n delay mask
-NLDLY  :: COutput_Flags{ .NL1, COutput_Flag_Bits(9) }
+NLDLY  :: transmute(COutput_Flags)tcflag_t(_NLDLY)
 // \r delay mask
-CRDLY  :: COutput_Flags{ .CR1, .CR2, .CR3 }
+CRDLY  :: transmute(COutput_Flags)tcflag_t(_CRDLY)
 // horizontal tab delay mask
-TABDLY :: COutput_Flags{ .TAB1, .TAB3, COutput_Flag_Bits(2) }
+TABDLY :: transmute(COutput_Flags)tcflag_t(_TABDLY)
 // \b delay mask
-BSDLY  :: COutput_Flags{ .BS1 }
+BSDLY  :: transmute(COutput_Flags)tcflag_t(_BSDLY)
 // vertical tab delay mask
-VTDLY  :: COutput_Flags{ .VT1 }
+VTDLY  :: transmute(COutput_Flags)tcflag_t(_VTDLY)
 // form feed delay mask
-FFDLY  :: COutput_Flags{ .FF1 }
+FFDLY  :: transmute(COutput_Flags)tcflag_t(_FFDLY)
 
 speed_t :: enum _speed_t {
 	B0     = B0,
@@ -596,6 +597,4 @@ when ODIN_OS == .Darwin {
 	TCOOFF :: 0
 	TCOON  :: 1
 
-} else {
-	#panic("posix is unimplemented for the current target")
 }

+ 13 - 2
core/sys/posix/time.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "core:c"
@@ -229,6 +230,16 @@ when ODIN_OS == .Darwin {
 
 	getdate_err: Errno = .ENOSYS // NOTE: looks like it's not a thing on OpenBSD.
 
-} else {
-	#panic("posix is unimplemented for the current target")
+} else when ODIN_OS == .Linux {
+
+	clockid_t :: distinct c.int
+
+	CLOCK_MONOTONIC          :: 1
+	CLOCK_PROCESS_CPUTIME_ID :: 2
+	CLOCK_REALTIME           :: 0
+	CLOCK_THREAD_CPUTIME_ID  :: 3
+
+	foreign lib {
+		getdate_err: Errno
+	}
 }

+ 1 - 2
core/sys/posix/ulimit.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "core:c"
@@ -38,6 +39,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 
 	// NOTE: I don't think OpenBSD implements this API.
 
-} else {
-	#panic("posix is unimplemented for the current target")
 }

+ 2 - 110
core/sys/posix/unistd.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "core:c"
@@ -11,19 +12,6 @@ when ODIN_OS == .Darwin {
 // 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`.
 
@@ -42,18 +30,6 @@ foreign lib {
 	*/
 	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.
 
@@ -204,15 +180,6 @@ foreign lib {
 	*/
 	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.
@@ -392,44 +359,6 @@ foreign lib {
 	*/
 	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 cwd == nil {
-				errno := posix.errno()
-				if 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.
 
@@ -829,13 +758,6 @@ foreign lib {
 	*/
 	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.
 
@@ -912,13 +834,6 @@ foreign lib {
 	*/
 	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.
 
@@ -958,13 +873,6 @@ foreign lib {
 	*/
 	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.
 
@@ -973,20 +881,6 @@ foreign lib {
 	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,
@@ -2063,6 +1957,7 @@ when ODIN_OS == .Darwin {
 	_SC_TYPED_MEMORY_OBJECTS   :: 165
 	_SC_2_PBS                  :: 168
 	_SC_2_PBS_ACCOUNTING       :: 169
+	_SC_2_PBS_LOCATE           :: 170
 	_SC_2_PBS_MESSAGE          :: 171
 	_SC_2_PBS_TRACK            :: 172
 	_SC_SYMLOOP_MAX            :: 173
@@ -2097,7 +1992,4 @@ when ODIN_OS == .Darwin {
 	// NOTE: Not implemented.
 	_POSIX_VDISABLE :: 0
 
-} else {
-	#panic("posix is unimplemented for the current target")
 }
-

+ 153 - 0
core/sys/posix/unistd_libc.odin

@@ -0,0 +1,153 @@
+#+build linux, windows, darwin, netbsd, openbsd, freebsd
+package posix
+
+import "core:c"
+
+when ODIN_OS == .Windows {
+	foreign import lib "system:libucrt.lib"
+} else 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 ]]
+	*/
+	@(link_name=LACCESS)
+	access :: proc(path: cstring, amode: Mode_Flags = F_OK) -> result ---
+
+	/*
+	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 ]]
+	*/
+	@(link_name=LCHDIR)
+	chdir :: proc(path: cstring) -> result ---
+
+	/*
+	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) -> ! ---
+
+	/*
+	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 cwd == nil {
+				errno := posix.errno()
+				if 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 ]]
+	*/
+	@(link_name=LGETCWD)
+	getcwd :: proc(buf: [^]c.char, size: c.size_t) -> cstring ---
+
+	/*
+	Remove an (empty) directory.
+
+	]] More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/rmdir.html ]]
+	*/
+	@(link_name=LRMDIR)
+	rmdir :: proc(path: cstring) -> result ---
+
+	/*
+	Copy nbyte bytes, from src, to dest, exchanging adjecent bytes.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/swab.html ]]
+	*/
+	@(link_name=LSWAB)
+	swab :: proc(src: [^]byte, dest: [^]byte, nbytes: c.ssize_t) ---
+
+	/*
+	Remove a directory entry.
+
+	[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlink.html ]]
+	*/
+	@(link_name=LUNLINK)
+	unlink :: proc(path: cstring) -> result ---
+}
+
+when ODIN_OS == .Windows {
+	@(private) LACCESS :: "_access"
+	@(private) LCHDIR  :: "_chdir"
+	@(private) LGETCWD :: "_getcwd"
+	@(private) LRMDIR  :: "_rmdir"
+	@(private) LSWAB   :: "_swab"
+	@(private) LUNLINK :: "_unlink"
+} else {
+	@(private) LACCESS :: "access"
+	@(private) LCHDIR  :: "chdir"
+	@(private) LGETCWD :: "getcwd"
+	@(private) LRMDIR  :: "rmdir"
+	@(private) LSWAB   :: "swab"
+	@(private) LUNLINK :: "unlink"
+}
+
+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{}
+
+when ODIN_OS == .Windows {
+	_F_OK :: 0
+	X_OK  :: 1
+	W_OK  :: 2
+	R_OK  :: 4
+	#assert(W_OK|R_OK == 6)
+}

+ 1 - 2
core/sys/posix/utime.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 when ODIN_OS == .Darwin {
@@ -31,6 +32,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
 		modtime: time_t, /* [PSX] modification time (seconds since epoch) */
 	}
 
-} else {
-	#panic("posix is unimplemented for the current target")
 }

+ 1 - 2
core/sys/posix/wordexp.odin

@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
 package posix
 
 import "core:c"
@@ -123,6 +124,4 @@ when ODIN_OS == .Darwin {
 	WRDE_CMDSUB  :: 4
 	WRDE_SYNTAX  :: 5
 
-} else {
-	#panic("posix is unimplemented for the current target")
 }

+ 2 - 15
tests/core/sys/posix/posix.odin

@@ -1,4 +1,4 @@
-#+build darwin, freebsd, openbsd, netbsd
+#+build linux, darwin, freebsd, openbsd, netbsd
 package tests_core_posix
 
 import "core:log"
@@ -144,7 +144,6 @@ test_libgen :: proc(t: ^testing.T) {
 		{ "usr/",             ".",          "usr" },
 		{ "",                 ".",          "." },
 		{ "/",                "/",          "/" },
-		{ "//",               "/",          "/" },
 		{ "///",              "/",          "/" },
 		{ "/usr/",            "/",          "usr" },
 		{ "/usr/lib",         "/usr",       "lib" },
@@ -197,24 +196,12 @@ test_stat :: proc(t: ^testing.T) {
 	stat: posix.stat_t
 	testing.expect_value(t, posix.stat(#file, &stat), posix.result.OK)
 	testing.expect(t, posix.S_ISREG(stat.st_mode))
-	testing.expect_value(t, stat.st_mode, posix.mode_t{.IROTH, .IRGRP, .IRUSR, .IWUSR, .IFREG})
+	testing.expect_value(t, stat.st_mode, posix.mode_t{.IROTH, .IRGRP, .IRUSR, .IWGRP, .IWUSR, .IFREG})
 
 	CONTENT := #load(#file)
 	testing.expect_value(t, stat.st_size, posix.off_t(len(CONTENT)))
 }
 
-@(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)
-}
-
 @(test)
 test_pthreads :: proc(t: ^testing.T) {
 	testing.set_fail_timeout(t, time.Second)

+ 1 - 1
tests/core/sys/posix/structs.odin

@@ -1,4 +1,4 @@
-#+build darwin, freebsd, openbsd, netbsd
+#+build linux, darwin, freebsd, openbsd, netbsd
 package tests_core_posix
 
 import "core:log"

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

@@ -40,7 +40,9 @@ int main(int argc, char *argv[])
     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));
 
+#ifndef __linux__
     printf("sched_param %zu %zu\n", sizeof(struct sched_param), _Alignof(struct sched_param));
+#endif
 
 	printf("termios %zu %zu\n", sizeof(struct termios), _Alignof(struct termios));
 

+ 5 - 1
tests/core/sys/posix/structs/structs.odin

@@ -14,7 +14,11 @@ main :: proc() {
 	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))
+	// NOTE: On Linux, differences between libc may mean the Odin side is larger than the other side,
+	// this is fine in practice.
+	when ODIN_OS != .Linux {
+		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))