aboutsummaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorColin Davidson <colrdavidson@gmail.com>2024-11-20 15:51:08 -0800
committerColin Davidson <colrdavidson@gmail.com>2024-11-20 15:51:08 -0800
commitd60fb5a44e4d2e371562fd38947f8125b06bceb9 (patch)
tree4e924ee102c2af7b30d29017ab716ed00c51ab26 /core
parentf3ab14b8ccb45d0fef8a96937635bdf0943ce7d6 (diff)
parent3229f4668dfaa5f43a374bc549f42661b002699d (diff)
update to master
Diffstat (limited to 'core')
-rw-r--r--core/c/frontend/tokenizer/tokenizer.odin2
-rw-r--r--core/c/libc/README.md4
-rw-r--r--core/c/libc/locale.odin133
-rw-r--r--core/c/libc/stdatomic.odin2
-rw-r--r--core/compress/zlib/doc.odin3
-rw-r--r--core/container/bit_array/bit_array.odin41
-rw-r--r--core/crypto/_chacha20/simd128/chacha20_simd128.odin2
-rw-r--r--core/crypto/_sha3/sp800_185.odin14
-rw-r--r--core/dynlib/lib.odin10
-rw-r--r--core/dynlib/lib_js.odin8
-rw-r--r--core/dynlib/lib_unix.odin30
-rw-r--r--core/dynlib/lib_windows.odin8
-rw-r--r--core/encoding/cbor/cbor.odin2
-rw-r--r--core/encoding/cbor/unmarshal.odin25
-rw-r--r--core/encoding/hxa/read.odin2
-rw-r--r--core/encoding/ini/ini.odin1
-rw-r--r--core/encoding/json/marshal.odin6
-rw-r--r--core/encoding/json/unmarshal.odin62
-rw-r--r--core/fmt/fmt.odin14
-rw-r--r--core/image/general.odin12
-rw-r--r--core/io/util.odin2
-rw-r--r--core/math/linalg/general.odin6
-rw-r--r--core/math/math_sincos.odin12
-rw-r--r--core/math/rand/rand.odin79
-rw-r--r--core/mem/allocators.odin334
-rw-r--r--core/mem/mem.odin4
-rw-r--r--core/mem/mutex_allocator.odin2
-rw-r--r--core/mem/tracking_allocator.odin2
-rw-r--r--core/mem/virtual/virtual_posix.odin10
-rw-r--r--core/net/socket_darwin.odin5
-rw-r--r--core/net/socket_freebsd.odin5
-rw-r--r--core/net/socket_linux.odin37
-rw-r--r--core/net/socket_windows.odin5
-rw-r--r--core/odin/ast/ast.odin23
-rw-r--r--core/odin/ast/clone.odin3
-rw-r--r--core/odin/parser/parser.odin47
-rw-r--r--core/odin/tokenizer/tokenizer.odin2
-rw-r--r--core/os/os2/allocators.odin2
-rw-r--r--core/os/os2/dir_linux.odin2
-rw-r--r--core/os/os2/dir_windows.odin10
-rw-r--r--core/os/os2/errors_linux.odin2
-rw-r--r--core/os/os2/errors_posix.odin2
-rw-r--r--core/os/os2/errors_windows.odin4
-rw-r--r--core/os/os2/file_util.odin2
-rw-r--r--core/os/os2/file_windows.odin6
-rw-r--r--core/os/os2/path_posix.odin2
-rw-r--r--core/os/os2/pipe.odin37
-rw-r--r--core/os/os2/pipe_linux.odin26
-rw-r--r--core/os/os2/pipe_posix.odin27
-rw-r--r--core/os/os2/pipe_windows.odin12
-rw-r--r--core/os/os2/process.odin356
-rw-r--r--core/os/os2/process_linux.odin2
-rw-r--r--core/os/os2/process_posix.odin3
-rw-r--r--core/os/os2/process_posix_other.odin1
-rw-r--r--core/os/os2/process_windows.odin40
-rw-r--r--core/os/os2/stat_windows.odin15
-rw-r--r--core/os/os_darwin.odin6
-rw-r--r--core/os/os_freebsd.odin7
-rw-r--r--core/os/os_haiku.odin6
-rw-r--r--core/os/os_linux.odin19
-rw-r--r--core/os/os_netbsd.odin6
-rw-r--r--core/os/os_openbsd.odin6
-rw-r--r--core/path/filepath/match.odin7
-rw-r--r--core/path/filepath/path_unix.odin37
-rw-r--r--core/reflect/reflect.odin102
-rw-r--r--core/reflect/types.odin32
-rw-r--r--core/simd/x86/sse2.odin4
-rw-r--r--core/slice/map.odin8
-rw-r--r--core/slice/slice.odin166
-rw-r--r--core/strconv/strconv.odin3
-rw-r--r--core/strings/strings.odin2
-rw-r--r--core/sys/darwin/Foundation/NSApplication.odin10
-rw-r--r--core/sys/darwin/mach_darwin.odin514
-rw-r--r--core/sys/info/cpu_intel.odin47
-rw-r--r--core/sys/info/platform_darwin.odin16
-rw-r--r--core/sys/linux/bits.odin127
-rw-r--r--core/sys/linux/constants.odin25
-rw-r--r--core/sys/linux/sys.odin25
-rw-r--r--core/sys/linux/types.odin23
-rw-r--r--core/sys/posix/arpa_inet.odin1
-rw-r--r--core/sys/posix/dirent.odin87
-rw-r--r--core/sys/posix/dlfcn.odin12
-rw-r--r--core/sys/posix/errno.odin183
-rw-r--r--core/sys/posix/fcntl.odin152
-rw-r--r--core/sys/posix/fnmatch.odin11
-rw-r--r--core/sys/posix/glob.odin36
-rw-r--r--core/sys/posix/grp.odin5
-rw-r--r--core/sys/posix/iconv.odin1
-rw-r--r--core/sys/posix/langinfo.odin89
-rw-r--r--core/sys/posix/libgen.odin8
-rw-r--r--core/sys/posix/limits.odin98
-rw-r--r--core/sys/posix/locale.odin94
-rw-r--r--core/sys/posix/monetary.odin1
-rw-r--r--core/sys/posix/net_if.odin5
-rw-r--r--core/sys/posix/netdb.odin39
-rw-r--r--core/sys/posix/netinet_in.odin93
-rw-r--r--core/sys/posix/netinet_tcp.odin5
-rw-r--r--core/sys/posix/poll.odin24
-rw-r--r--core/sys/posix/posix.odin7
-rw-r--r--core/sys/posix/pthread.odin111
-rw-r--r--core/sys/posix/pwd.odin15
-rw-r--r--core/sys/posix/sched.odin5
-rw-r--r--core/sys/posix/setjmp.odin6
-rw-r--r--core/sys/posix/setjmp_libc.odin11
-rw-r--r--core/sys/posix/signal.odin298
-rw-r--r--core/sys/posix/signal_libc.odin145
-rw-r--r--core/sys/posix/stdio.odin173
-rw-r--r--core/sys/posix/stdio_libc.odin207
-rw-r--r--core/sys/posix/stdlib.odin79
-rw-r--r--core/sys/posix/stdlib_libc.odin101
-rw-r--r--core/sys/posix/string.odin15
-rw-r--r--core/sys/posix/string_libc.odin30
-rw-r--r--core/sys/posix/sys_ipc.odin29
-rw-r--r--core/sys/posix/sys_mman.odin30
-rw-r--r--core/sys/posix/sys_msg.odin53
-rw-r--r--core/sys/posix/sys_resource.odin18
-rw-r--r--core/sys/posix/sys_select.odin7
-rw-r--r--core/sys/posix/sys_sem.odin53
-rw-r--r--core/sys/posix/sys_shm.odin47
-rw-r--r--core/sys/posix/sys_socket.odin227
-rw-r--r--core/sys/posix/sys_stat.odin187
-rw-r--r--core/sys/posix/sys_statvfs.odin26
-rw-r--r--core/sys/posix/sys_time.odin5
-rw-r--r--core/sys/posix/sys_times.odin5
-rw-r--r--core/sys/posix/sys_uio.odin5
-rw-r--r--core/sys/posix/sys_un.odin10
-rw-r--r--core/sys/posix/sys_utsname.odin16
-rw-r--r--core/sys/posix/sys_wait.odin75
-rw-r--r--core/sys/posix/termios.odin17
-rw-r--r--core/sys/posix/time.odin15
-rw-r--r--core/sys/posix/ulimit.odin5
-rw-r--r--core/sys/posix/unistd.odin412
-rw-r--r--core/sys/posix/unistd_libc.odin153
-rw-r--r--core/sys/posix/utime.odin5
-rw-r--r--core/sys/posix/wordexp.odin24
-rw-r--r--core/sys/unix/pthread_darwin.odin96
-rw-r--r--core/sys/unix/pthread_freebsd.odin122
-rw-r--r--core/sys/unix/pthread_haiku.odin71
-rw-r--r--core/sys/unix/pthread_linux.odin124
-rw-r--r--core/sys/unix/pthread_netbsd.odin102
-rw-r--r--core/sys/unix/pthread_openbsd.odin74
-rw-r--r--core/sys/unix/pthread_unix.odin127
-rw-r--r--core/sys/unix/unix.odin8
-rw-r--r--core/sys/wasm/js/dom.odin2
-rw-r--r--core/sys/wasm/js/events.odin4
-rw-r--r--core/sys/wasm/js/odin.js49
-rw-r--r--core/sys/windows/icu.odin14
-rw-r--r--core/sys/windows/kernel32.odin8
-rw-r--r--core/sys/windows/types.odin17
-rw-r--r--core/sys/windows/user32.odin61
-rw-r--r--core/sys/windows/ux_theme.odin2
-rw-r--r--core/sys/windows/winerror.odin14
-rw-r--r--core/testing/signal_handler_libc.odin21
-rw-r--r--core/testing/signal_handler_posix.odin22
-rw-r--r--core/testing/signal_handler_windows.odin6
-rw-r--r--core/text/scanner/scanner.odin2
-rw-r--r--core/thread/thread_unix.odin70
-rw-r--r--core/time/datetime/constants.odin45
-rw-r--r--core/time/datetime/datetime.odin6
-rw-r--r--core/time/time.odin4
-rw-r--r--core/time/time_linux.odin6
-rw-r--r--core/time/timezone/tz_unix.odin89
-rw-r--r--core/time/timezone/tz_windows.odin311
-rw-r--r--core/time/timezone/tzdate.odin341
-rw-r--r--core/time/timezone/tzif.odin652
-rw-r--r--core/unicode/utf16/utf16.odin14
-rw-r--r--core/unicode/utf8/utf8string/string.odin2
167 files changed, 6143 insertions, 2622 deletions
diff --git a/core/c/frontend/tokenizer/tokenizer.odin b/core/c/frontend/tokenizer/tokenizer.odin
index 2415e06a0..558077717 100644
--- a/core/c/frontend/tokenizer/tokenizer.odin
+++ b/core/c/frontend/tokenizer/tokenizer.odin
@@ -291,7 +291,7 @@ scan_escape :: proc(t: ^Tokenizer) -> bool {
n -= 1
}
- if x > max || 0xd800 <= x && x <= 0xe000 {
+ if x > max || 0xd800 <= x && x <= 0xdfff {
error_offset(t, offset, "escape sequence is an invalid Unicode code point")
return false
}
diff --git a/core/c/libc/README.md b/core/c/libc/README.md
index 95053b963..08e789757 100644
--- a/core/c/libc/README.md
+++ b/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 <weilercdale@gmail.com>.
-``` \ No newline at end of file
+```
diff --git a/core/c/libc/locale.odin b/core/c/libc/locale.odin
new file mode 100644
index 000000000..371d755c5
--- /dev/null
+++ b/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
+
+}
diff --git a/core/c/libc/stdatomic.odin b/core/c/libc/stdatomic.odin
index 8dc243b78..43a0e51df 100644
--- a/core/c/libc/stdatomic.odin
+++ b/core/c/libc/stdatomic.odin
@@ -235,7 +235,7 @@ atomic_compare_exchange_weak :: #force_inline proc(object, expected: ^$T, desire
return ok
}
-atomic_compare_exchange_weak_explicit :: #force_inline proc(object, expected: ^$T, desited: T, success, failure: memory_order) -> bool {
+atomic_compare_exchange_weak_explicit :: #force_inline proc(object, expected: ^$T, desired: T, success, failure: memory_order) -> bool {
assert(failure != .release)
assert(failure != .acq_rel)
diff --git a/core/compress/zlib/doc.odin b/core/compress/zlib/doc.odin
index 0a5b4eb40..6ae537a87 100644
--- a/core/compress/zlib/doc.odin
+++ b/core/compress/zlib/doc.odin
@@ -13,6 +13,7 @@ Example:
package main
import "core:bytes"
+ import "core:compress/zlib"
import "core:fmt"
main :: proc() {
@@ -36,7 +37,7 @@ Example:
buf: bytes.Buffer
// We can pass ", true" to inflate a raw DEFLATE stream instead of a ZLIB wrapped one.
- err := inflate(input=ODIN_DEMO, buf=&buf, expected_output_size=OUTPUT_SIZE)
+ err := zlib.inflate(input=ODIN_DEMO, buf=&buf, expected_output_size=OUTPUT_SIZE)
defer bytes.buffer_destroy(&buf)
if err != nil {
diff --git a/core/container/bit_array/bit_array.odin b/core/container/bit_array/bit_array.odin
index 9a76dc78f..85b941d12 100644
--- a/core/container/bit_array/bit_array.odin
+++ b/core/container/bit_array/bit_array.odin
@@ -259,6 +259,7 @@ Inputs:
unsafe_unset :: proc(b: ^Bit_Array, bit: int) #no_bounds_check {
b.bits[bit >> INDEX_SHIFT] &~= 1 << uint(bit & INDEX_MASK)
}
+
/*
A helper function to create a Bit Array with optional bias, in case your smallest index is non-zero (including negative).
@@ -276,22 +277,50 @@ Returns:
- ba: Allocates a bit_Array, backing data is set to `max-min / 64` indices, rounded up (eg 65 - 0 allocates for [2]u64).
*/
create :: proc(max_index: int, min_index: int = 0, allocator := context.allocator) -> (res: ^Bit_Array, ok: bool) #optional_ok {
- context.allocator = allocator
size_in_bits := max_index - min_index
if size_in_bits < 0 { return {}, false }
+ res = new(Bit_Array, allocator)
+ ok = init(res, max_index, min_index, allocator)
+ res.free_pointer = true
+
+ if !ok { free(res, allocator) }
+
+ return
+}
+
+/*
+A helper function to initialize a Bit Array with optional bias, in case your smallest index is non-zero (including negative).
+
+The range of bits created by this procedure is `min_index..<max_index`, and the
+array will be able to expand beyond `max_index` if needed.
+
+*Allocates (`make(ba.bits)`)*
+
+Inputs:
+- max_index: maximum starting index
+- min_index: minimum starting index (used as a bias)
+- allocator: (default is context.allocator)
+*/
+init :: proc(res: ^Bit_Array, max_index: int, min_index: int = 0, allocator := context.allocator) -> (ok: bool) {
+ size_in_bits := max_index - min_index
+
+ if size_in_bits < 0 { return false }
+
legs := size_in_bits >> INDEX_SHIFT
- if size_in_bits & INDEX_MASK > 0 {legs+=1}
- bits, err := make([dynamic]u64, legs)
- ok = err == mem.Allocator_Error.None
- res = new(Bit_Array)
+ if size_in_bits & INDEX_MASK > 0 { legs += 1 }
+
+ bits, err := make([dynamic]u64, legs, allocator)
+ ok = err == nil
+
res.bits = bits
res.bias = min_index
res.length = max_index - min_index
- res.free_pointer = true
+ res.free_pointer = false
return
}
+
/*
Sets all values in the Bit_Array to zero.
diff --git a/core/crypto/_chacha20/simd128/chacha20_simd128.odin b/core/crypto/_chacha20/simd128/chacha20_simd128.odin
index 2f91ac52a..fe0d0d518 100644
--- a/core/crypto/_chacha20/simd128/chacha20_simd128.odin
+++ b/core/crypto/_chacha20/simd128/chacha20_simd128.odin
@@ -51,7 +51,7 @@ _ROT_16: simd.u32x4 : {16, 16, 16, 16}
when ODIN_ENDIAN == .Big {
@(private = "file")
- _increment_counter :: #force_inline proc "contextless" (ctx: ^Context) -> simd.u32x4 {
+ _increment_counter :: #force_inline proc "contextless" (ctx: ^_chacha20.Context) -> simd.u32x4 {
// In the Big Endian case, the low and high portions in the vector
// are flipped, so the 64-bit addition can't be done with a simple
// vector add.
diff --git a/core/crypto/_sha3/sp800_185.odin b/core/crypto/_sha3/sp800_185.odin
index f32398d5c..a96f78cc1 100644
--- a/core/crypto/_sha3/sp800_185.odin
+++ b/core/crypto/_sha3/sp800_185.odin
@@ -81,16 +81,18 @@ bytepad :: proc(ctx: ^Context, x_strings: [][]byte, w: int) {
// 2. while len(z) mod 8 ≠ 0:
// z = z || 0
- // 3. while (len(z)/8) mod w ≠ 0:
+ // 3. while (len(z)/8) mod w != 0:
// z = z || 00000000
z_len := u128(z_hi) << 64 | u128(z_lo)
z_rem := int(z_len % u128(w))
- pad := _PAD[:w - z_rem]
+ if z_rem != 0 {
+ pad := _PAD[:w - z_rem]
- // We just add the padding to the state, instead of returning z.
- //
- // 4. return z.
- update(ctx, pad)
+ // We just add the padding to the state, instead of returning z.
+ //
+ // 4. return z.
+ update(ctx, pad)
+ }
}
encode_string :: #force_inline proc(ctx: ^Context, s: []byte) -> (u64, u64) {
diff --git a/core/dynlib/lib.odin b/core/dynlib/lib.odin
index 09e16002d..84675a560 100644
--- a/core/dynlib/lib.odin
+++ b/core/dynlib/lib.odin
@@ -37,8 +37,8 @@ Example:
fmt.println("The library %q was successfully loaded", LIBRARY_PATH)
}
*/
-load_library :: proc(path: string, global_symbols := false) -> (library: Library, did_load: bool) {
- return _load_library(path, global_symbols)
+load_library :: proc(path: string, global_symbols := false, allocator := context.temp_allocator) -> (library: Library, did_load: bool) {
+ return _load_library(path, global_symbols, allocator)
}
/*
@@ -98,8 +98,8 @@ Example:
}
}
*/
-symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) #optional_ok {
- return _symbol_address(library, symbol)
+symbol_address :: proc(library: Library, symbol: string, allocator := context.temp_allocator) -> (ptr: rawptr, found: bool) #optional_ok {
+ return _symbol_address(library, symbol, allocator)
}
/*
@@ -174,4 +174,4 @@ initialize_symbols :: proc(
// Returns an error message for the last failed procedure call.
last_error :: proc() -> string {
return _last_error()
-} \ No newline at end of file
+}
diff --git a/core/dynlib/lib_js.odin b/core/dynlib/lib_js.odin
index 698cfee9c..b99143ba0 100644
--- a/core/dynlib/lib_js.odin
+++ b/core/dynlib/lib_js.odin
@@ -2,7 +2,9 @@
#+private
package dynlib
-_load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
+import "base:runtime"
+
+_load_library :: proc(path: string, global_symbols: bool, allocator: runtime.Allocator) -> (Library, bool) {
return nil, false
}
@@ -10,10 +12,10 @@ _unload_library :: proc(library: Library) -> bool {
return false
}
-_symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
+_symbol_address :: proc(library: Library, symbol: string, allocator: runtime.Allocator) -> (ptr: rawptr, found: bool) {
return nil, false
}
_last_error :: proc() -> string {
return ""
-} \ No newline at end of file
+}
diff --git a/core/dynlib/lib_unix.odin b/core/dynlib/lib_unix.odin
index f467d730d..337bf496d 100644
--- a/core/dynlib/lib_unix.odin
+++ b/core/dynlib/lib_unix.odin
@@ -2,28 +2,38 @@
#+private
package dynlib
-import "core:os"
+import "base:runtime"
-_load_library :: proc(path: string, global_symbols := false) -> (Library, bool) {
- flags := os.RTLD_NOW
+import "core:strings"
+import "core:sys/posix"
+
+_load_library :: proc(path: string, global_symbols: bool, allocator: runtime.Allocator) -> (Library, bool) {
+ flags := posix.RTLD_Flags{.NOW}
if global_symbols {
- flags |= os.RTLD_GLOBAL
+ flags += {.GLOBAL}
}
- lib := os.dlopen(path, flags)
+
+ cpath := strings.clone_to_cstring(path, allocator)
+ defer delete(cpath, allocator)
+
+ lib := posix.dlopen(cpath, flags)
return Library(lib), lib != nil
}
_unload_library :: proc(library: Library) -> bool {
- return os.dlclose(rawptr(library))
+ return posix.dlclose(posix.Symbol_Table(library)) == 0
}
-_symbol_address :: proc(library: Library, symbol: string) -> (ptr: rawptr, found: bool) {
- ptr = os.dlsym(rawptr(library), symbol)
+_symbol_address :: proc(library: Library, symbol: string, allocator: runtime.Allocator) -> (ptr: rawptr, found: bool) {
+ csymbol := strings.clone_to_cstring(symbol, allocator)
+ defer delete(csymbol, allocator)
+
+ ptr = posix.dlsym(posix.Symbol_Table(library), csymbol)
found = ptr != nil
return
}
_last_error :: proc() -> string {
- err := os.dlerror()
+ err := string(posix.dlerror())
return "unknown" if err == "" else err
-} \ No newline at end of file
+}
diff --git a/core/dynlib/lib_windows.odin b/core/dynlib/lib_windows.odin
index 6c41a1a75..928a1510d 100644
--- a/core/dynlib/lib_windows.odin
+++ b/core/dynlib/lib_windows.odin
@@ -2,11 +2,13 @@
#+private
package dynlib
+import "base:runtime"
+
import win32 "core:sys/windows"
import "core:strings"
import "core:reflect"
-_load_library :: proc(path: string, global_symbols := false, allocator := context.temp_allocator) -> (Library, bool) {
+_load_library :: proc(path: string, global_symbols: bool, allocator: runtime.Allocator) -> (Library, bool) {
// NOTE(bill): 'global_symbols' is here only for consistency with POSIX which has RTLD_GLOBAL
wide_path := win32.utf8_to_wstring(path, allocator)
defer free(wide_path, allocator)
@@ -19,7 +21,7 @@ _unload_library :: proc(library: Library) -> bool {
return bool(ok)
}
-_symbol_address :: proc(library: Library, symbol: string, allocator := context.temp_allocator) -> (ptr: rawptr, found: bool) {
+_symbol_address :: proc(library: Library, symbol: string, allocator: runtime.Allocator) -> (ptr: rawptr, found: bool) {
c_str := strings.clone_to_cstring(symbol, allocator)
defer delete(c_str, allocator)
ptr = win32.GetProcAddress(cast(win32.HMODULE)library, c_str)
@@ -31,4 +33,4 @@ _last_error :: proc() -> string {
err := win32.System_Error(win32.GetLastError())
err_msg := reflect.enum_string(err)
return "unknown" if err_msg == "" else err_msg
-} \ No newline at end of file
+}
diff --git a/core/encoding/cbor/cbor.odin b/core/encoding/cbor/cbor.odin
index 692be0020..8eb829ed3 100644
--- a/core/encoding/cbor/cbor.odin
+++ b/core/encoding/cbor/cbor.odin
@@ -563,7 +563,7 @@ to_json :: proc(val: Value, allocator := context.allocator) -> (json.Value, mem.
case: return false
}
}
- return false
+ return true
}
if keys_all_strings(v) {
diff --git a/core/encoding/cbor/unmarshal.odin b/core/encoding/cbor/unmarshal.odin
index bf27171f4..c39255d9d 100644
--- a/core/encoding/cbor/unmarshal.odin
+++ b/core/encoding/cbor/unmarshal.odin
@@ -442,9 +442,6 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
loc := #caller_location,
) -> (out_of_space: bool, err: Unmarshal_Error) {
for idx: uintptr = 0; length == -1 || idx < uintptr(length); idx += 1 {
- elem_ptr := rawptr(uintptr(da.data) + idx*uintptr(elemt.size))
- elem := any{elem_ptr, elemt.id}
-
hdr := _decode_header(d.reader) or_return
// Double size if out of capacity.
@@ -459,6 +456,10 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
if !ok { return false, .Out_Of_Memory }
}
+ // Set ptr after potential resizes to avoid invalidation.
+ elem_ptr := rawptr(uintptr(da.data) + idx*uintptr(elemt.size))
+ elem := any{elem_ptr, elemt.id}
+
err = _unmarshal_value(d, elem, hdr, allocator=allocator, loc=loc)
if length == -1 && err == .Break { break }
if err != nil { return }
@@ -509,7 +510,7 @@ _unmarshal_array :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header
raw := (^mem.Raw_Dynamic_Array)(v.data)
raw.data = raw_data(data)
raw.len = 0
- raw.cap = length
+ raw.cap = scap
raw.allocator = context.allocator
_ = assign_array(d, raw, t.elem, length) or_return
@@ -627,7 +628,8 @@ _unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header,
unknown := length == -1
fields := reflect.struct_fields_zipped(ti.id)
- for idx := 0; idx < len(fields) && (unknown || idx < length); idx += 1 {
+ idx := 0
+ for ; idx < len(fields) && (unknown || idx < length); idx += 1 {
// Decode key, keys can only be strings.
key: string
if keyv, kerr := decode_key(d, v, context.temp_allocator); unknown && kerr == .Break {
@@ -662,6 +664,8 @@ _unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header,
// Skips unused map entries.
if use_field_idx < 0 {
+ val := err_conv(_decode_from_decoder(d, allocator=context.temp_allocator)) or_return
+ destroy(val, context.temp_allocator)
continue
}
}
@@ -672,6 +676,17 @@ _unmarshal_map :: proc(d: Decoder, v: any, ti: ^reflect.Type_Info, hdr: Header,
fany := any{ptr, field.type.id}
_unmarshal_value(d, fany, _decode_header(r) or_return) or_return
}
+
+ // If there are fields left in the map that did not get decoded into the struct, decode and discard them.
+ if !unknown {
+ for _ in idx..<length {
+ key := err_conv(_decode_from_decoder(d, allocator=context.temp_allocator)) or_return
+ destroy(key, context.temp_allocator)
+ val := err_conv(_decode_from_decoder(d, allocator=context.temp_allocator)) or_return
+ destroy(val, context.temp_allocator)
+ }
+ }
+
return
case reflect.Type_Info_Map:
diff --git a/core/encoding/hxa/read.odin b/core/encoding/hxa/read.odin
index 5c8503229..a679946f8 100644
--- a/core/encoding/hxa/read.odin
+++ b/core/encoding/hxa/read.odin
@@ -117,7 +117,7 @@ read :: proc(data: []byte, filename := "<input>", print_error := false, allocato
layer.name = read_name(r) or_return
layer.components = read_value(r, u8) or_return
type := read_value(r, Layer_Data_Type) or_return
- if type > max(type) {
+ if type > max(Layer_Data_Type) {
if r.print_error {
fmt.eprintf("HxA Error: file '%s' has layer data type %d. Maximum value is %d\n",
r.filename, u8(type), u8(max(Layer_Data_Type)))
diff --git a/core/encoding/ini/ini.odin b/core/encoding/ini/ini.odin
index 2bb7996a3..c32b1deb5 100644
--- a/core/encoding/ini/ini.odin
+++ b/core/encoding/ini/ini.odin
@@ -154,6 +154,7 @@ write_section :: proc(w: io.Writer, name: string, n_written: ^int = nil) -> (n:
io.write_byte (w, '[', &n) or_return
io.write_string(w, name, &n) or_return
io.write_byte (w, ']', &n) or_return
+ io.write_byte (w, '\n', &n) or_return
return
}
diff --git a/core/encoding/json/marshal.odin b/core/encoding/json/marshal.odin
index 009bf7ade..f0f0927a1 100644
--- a/core/encoding/json/marshal.odin
+++ b/core/encoding/json/marshal.odin
@@ -192,12 +192,6 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
case runtime.Type_Info_Simd_Vector:
return .Unsupported_Type
-
- case runtime.Type_Info_Relative_Pointer:
- return .Unsupported_Type
-
- case runtime.Type_Info_Relative_Multi_Pointer:
- return .Unsupported_Type
case runtime.Type_Info_Matrix:
return .Unsupported_Type
diff --git a/core/encoding/json/unmarshal.odin b/core/encoding/json/unmarshal.odin
index 738e20c68..ea33badae 100644
--- a/core/encoding/json/unmarshal.odin
+++ b/core/encoding/json/unmarshal.odin
@@ -172,20 +172,33 @@ assign_float :: proc(val: any, f: $T) -> bool {
@(private)
-unmarshal_string_token :: proc(p: ^Parser, val: any, str: string, ti: ^reflect.Type_Info) -> bool {
+unmarshal_string_token :: proc(p: ^Parser, val: any, str: string, ti: ^reflect.Type_Info) -> (ok: bool, err: Error) {
val := val
switch &dst in val {
case string:
dst = str
- return true
+ return true, nil
case cstring:
if str == "" {
- dst = strings.clone_to_cstring("", p.allocator)
+ a_err: runtime.Allocator_Error
+ dst, a_err = strings.clone_to_cstring("", p.allocator)
+ #partial switch a_err {
+ case nil:
+ // okay
+ case .Out_Of_Memory:
+ err = .Out_Of_Memory
+ case:
+ err = .Invalid_Allocator
+ }
+ if err != nil {
+ return
+ }
} else {
// NOTE: This is valid because 'clone_string' appends a NUL terminator
dst = cstring(raw_data(str))
}
- return true
+ ok = true
+ return
}
#partial switch variant in ti.variant {
@@ -193,31 +206,37 @@ unmarshal_string_token :: proc(p: ^Parser, val: any, str: string, ti: ^reflect.T
for name, i in variant.names {
if name == str {
assign_int(val, variant.values[i])
- return true
+ return true, nil
}
}
// TODO(bill): should this be an error or not?
- return true
+ return true, nil
case reflect.Type_Info_Integer:
- i := strconv.parse_i128(str) or_return
+ i, pok := strconv.parse_i128(str)
+ if !pok {
+ return false, nil
+ }
if assign_int(val, i) {
- return true
+ return true, nil
}
if assign_float(val, i) {
- return true
+ return true, nil
}
case reflect.Type_Info_Float:
- f := strconv.parse_f64(str) or_return
+ f, pok := strconv.parse_f64(str)
+ if !pok {
+ return false, nil
+ }
if assign_int(val, f) {
- return true
+ return true, nil
}
if assign_float(val, f) {
- return true
+ return true, nil
}
}
- return false
+ return false, nil
}
@@ -304,7 +323,7 @@ unmarshal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
case .Ident:
advance_token(p)
if p.spec == .MJSON {
- if unmarshal_string_token(p, any{v.data, ti.id}, token.text, ti) {
+ if unmarshal_string_token(p, any{v.data, ti.id}, token.text, ti) or_return {
return nil
}
}
@@ -312,13 +331,18 @@ unmarshal_value :: proc(p: ^Parser, v: any) -> (err: Unmarshal_Error) {
case .String:
advance_token(p)
- str := unquote_string(token, p.spec, p.allocator) or_return
- if unmarshal_string_token(p, any{v.data, ti.id}, str, ti) {
- return nil
+ str := unquote_string(token, p.spec, p.allocator) or_return
+ dest := any{v.data, ti.id}
+ if !(unmarshal_string_token(p, dest, str, ti) or_return) {
+ delete(str, p.allocator)
+ return UNSUPPORTED_TYPE
}
- delete(str, p.allocator)
- return UNSUPPORTED_TYPE
+ switch destv in dest {
+ case string, cstring:
+ case: delete(str, p.allocator)
+ }
+ return nil
case .Open_Brace:
return unmarshal_object(p, v, .Close_Brace)
diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin
index 0d11ad8a9..49e9f2e6d 100644
--- a/core/fmt/fmt.odin
+++ b/core/fmt/fmt.odin
@@ -1531,7 +1531,7 @@ fmt_pointer :: proc(fi: ^Info, p: rawptr, verb: rune) {
case 'o': _fmt_int(fi, u, 8, false, 8*size_of(rawptr), __DIGITS_UPPER)
case 'i', 'd': _fmt_int(fi, u, 10, false, 8*size_of(rawptr), __DIGITS_UPPER)
case 'z': _fmt_int(fi, u, 12, false, 8*size_of(rawptr), __DIGITS_UPPER)
- case 'x': _fmt_int(fi, u, 16, false, 8*size_of(rawptr), __DIGITS_UPPER)
+ case 'x': _fmt_int(fi, u, 16, false, 8*size_of(rawptr), __DIGITS_LOWER)
case 'X': _fmt_int(fi, u, 16, false, 8*size_of(rawptr), __DIGITS_UPPER)
case:
@@ -3002,18 +3002,6 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
case runtime.Type_Info_Bit_Set:
fmt_bit_set(fi, v, verb = verb)
- case runtime.Type_Info_Relative_Pointer:
- ptr := reflect.relative_pointer_to_absolute_raw(v.data, info.base_integer.id)
- absolute_ptr := any{ptr, info.pointer.id}
-
- fmt_value(fi, absolute_ptr, verb)
-
- case runtime.Type_Info_Relative_Multi_Pointer:
- ptr := reflect.relative_pointer_to_absolute_raw(v.data, info.base_integer.id)
- absolute_ptr := any{ptr, info.pointer.id}
-
- fmt_value(fi, absolute_ptr, verb)
-
case runtime.Type_Info_Matrix:
fmt_matrix(fi, v, verb, info)
diff --git a/core/image/general.odin b/core/image/general.odin
index 17a8f35ea..c4a884071 100644
--- a/core/image/general.odin
+++ b/core/image/general.odin
@@ -23,7 +23,15 @@ register :: proc(kind: Which_File_Type, loader: Loader_Proc, destroyer: Destroy_
load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
loader := _internal_loaders[which(data)]
if loader == nil {
- return nil, .Unsupported_Format
+
+ // Check if there is at least one loader, otherwise panic to let the user know about misuse.
+ for a_loader in _internal_loaders {
+ if a_loader != nil {
+ return nil, .Unsupported_Format
+ }
+ }
+
+ panic("image.load called when no image loaders are registered. Register a loader by first importing a subpackage (eg: `import \"core:image/png\"`), or with image.register")
}
return loader(data, options, allocator)
}
@@ -185,7 +193,7 @@ which_bytes :: proc(data: []byte) -> Which_File_Type {
return .HDR
case s[:4] == "\x38\x42\x50\x53":
return .PSD
- case s[:4] != "\x53\x80\xF6\x34" && s[88:92] == "PICT":
+ case s[:4] == "\x53\x80\xF6\x34" && s[88:92] == "PICT":
return .PIC
case s[:4] == "\x69\x63\x6e\x73":
return .ICNS
diff --git a/core/io/util.odin b/core/io/util.odin
index e65a69fb3..296be7bc0 100644
--- a/core/io/util.odin
+++ b/core/io/util.odin
@@ -225,7 +225,7 @@ write_escaped_rune :: proc(w: Writer, r: rune, quote: byte, html_safe := false,
} else {
write_byte(w, '\\', &n) or_return
write_byte(w, 'U', &n) or_return
- for s := 24; s >= 0; s -= 4 {
+ for s := 28; s >= 0; s -= 4 {
write_byte(w, DIGITS_LOWER[c>>uint(s) & 0xf], &n) or_return
}
}
diff --git a/core/math/linalg/general.odin b/core/math/linalg/general.odin
index 7587ad63f..f82d75bff 100644
--- a/core/math/linalg/general.odin
+++ b/core/math/linalg/general.odin
@@ -53,15 +53,15 @@ vector_dot :: proc "contextless" (a, b: $T/[$N]$E) -> (c: E) where IS_NUMERIC(E)
}
@(require_results)
quaternion64_dot :: proc "contextless" (a, b: $T/quaternion64) -> (c: f16) {
- return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z
+ return a.w*b.w + a.x*b.x + a.y*b.y + a.z*b.z
}
@(require_results)
quaternion128_dot :: proc "contextless" (a, b: $T/quaternion128) -> (c: f32) {
- return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z
+ return a.w*b.w + a.x*b.x + a.y*b.y + a.z*b.z
}
@(require_results)
quaternion256_dot :: proc "contextless" (a, b: $T/quaternion256) -> (c: f64) {
- return a.w*a.w + a.x*b.x + a.y*b.y + a.z*b.z
+ return a.w*b.w + a.x*b.x + a.y*b.y + a.z*b.z
}
dot :: proc{scalar_dot, vector_dot, quaternion64_dot, quaternion128_dot, quaternion256_dot}
diff --git a/core/math/math_sincos.odin b/core/math/math_sincos.odin
index b616f410d..9b4d3de40 100644
--- a/core/math/math_sincos.odin
+++ b/core/math/math_sincos.odin
@@ -189,12 +189,12 @@ sincos_f64 :: proc "contextless" (x: f64) -> (sin, cos: f64) #no_bounds_check {
// sin coefficients
@(private="file")
_sin := [?]f64{
- 0h3de5d8fd1fd19ccd, // 1.58962301576546568060e-10
- 0hbe5ae5e5a9291f5d, // -2.50507477628578072866e-8
- 0h3ec71de3567d48a1, // 2.75573136213857245213e-6
- 0hbf2a01a019bfdf03, // -1.98412698295895385996e-4
- 0h3f8111111110f7d0, // 8.33333333332211858878e-3
- 0hbfc5555555555548, // -1.66666666666666307295e-1
+ 0h3de5d8fd1fd19ccd, // 1.58962301576546568060e-10
+ 0hbe5ae5e5a9291f5d, // -2.50507477628578072866e-8
+ 0h3ec71de3567d48a1, // 2.75573136213857245213e-6
+ 0hbf2a01a019bfdf03, // -1.98412698295895385996e-4
+ 0h3f8111111110f7d0, // 8.33333333332211858878e-3
+ 0hbfc5555555555548, // -1.66666666666666307295e-1
}
// cos coefficients
diff --git a/core/math/rand/rand.odin b/core/math/rand/rand.odin
index 61301cf8a..474277e84 100644
--- a/core/math/rand/rand.odin
+++ b/core/math/rand/rand.odin
@@ -670,20 +670,69 @@ choice :: proc(array: $T/[]$E, gen := context.random_generator) -> (res: E) {
@(require_results)
-choice_enum :: proc($T: typeid, gen := context.random_generator) -> T
- where
- intrinsics.type_is_enum(T),
- size_of(T) <= 8,
- len(T) == cap(T) /* Only allow contiguous enum types */ \
-{
- when intrinsics.type_is_unsigned(intrinsics.type_core_type(T)) &&
- u64(max(T)) > u64(max(i64)) {
- i := uint64(gen) % u64(len(T))
- i += u64(min(T))
- return T(i)
+choice_enum :: proc($T: typeid, gen := context.random_generator) -> T where intrinsics.type_is_enum(T) {
+ when size_of(T) <= 8 && len(T) == cap(T) {
+ when intrinsics.type_is_unsigned(intrinsics.type_core_type(T)) &&
+ u64(max(T)) > u64(max(i64)) {
+ i := uint64(gen) % u64(len(T))
+ i += u64(min(T))
+ return T(i)
+ } else {
+ i := int63_max(i64(len(T)), gen)
+ i += i64(min(T))
+ return T(i)
+ }
} else {
- i := int63_max(i64(len(T)), gen)
- i += i64(min(T))
- return T(i)
+ values := runtime.type_info_base(type_info_of(T)).variant.(runtime.Type_Info_Enum).values
+ return T(choice(values))
+ }
+}
+
+/*
+Returns a random *set* bit from the provided `bit_set`.
+
+Inputs:
+- set: The `bit_set` to choose a random set bit from
+
+Returns:
+- res: The randomly selected bit, or the zero value if `ok` is `false`
+- ok: Whether the bit_set was not empty and thus `res` is actually a random set bit
+
+Example:
+ import "core:math/rand"
+ import "core:fmt"
+
+ choice_bit_set_example :: proc() {
+ Flags :: enum {
+ A,
+ B = 10,
+ C,
+ }
+
+ fmt.println(rand.choice_bit_set(bit_set[Flags]{}))
+ fmt.println(rand.choice_bit_set(bit_set[Flags]{.B}))
+ fmt.println(rand.choice_bit_set(bit_set[Flags]{.B, .C}))
+ fmt.println(rand.choice_bit_set(bit_set[0..<15]{5, 1, 4}))
}
-} \ No newline at end of file
+
+Possible Output:
+ A false
+ B true
+ C true
+ 5 true
+*/
+@(require_results)
+choice_bit_set :: proc(set: $T/bit_set[$E], gen := context.random_generator) -> (res: E, ok: bool) {
+ total_set := card(set)
+ if total_set == 0 {
+ return {}, false
+ }
+
+ core_set := transmute(intrinsics.type_bit_set_underlying_type(T))set
+
+ for target := int_max(total_set, gen); target > 0; target -= 1 {
+ core_set &= core_set - 1
+ }
+
+ return E(intrinsics.count_trailing_zeros(core_set)), true
+}
diff --git a/core/mem/allocators.odin b/core/mem/allocators.odin
index d729b902c..13d509f1e 100644
--- a/core/mem/allocators.odin
+++ b/core/mem/allocators.odin
@@ -14,16 +14,16 @@ but an attempt to allocate memory is not an error.
nil_allocator :: proc() -> Allocator {
return Allocator{
procedure = nil_allocator_proc,
- data = nil,
+ data = nil,
}
}
nil_allocator_proc :: proc(
- allocator_data: rawptr,
- mode: Allocator_Mode,
+ allocator_data: rawptr,
+ mode: Allocator_Mode,
size, alignment: int,
- old_memory: rawptr,
- old_size: int,
+ old_memory: rawptr,
+ old_size: int,
loc := #caller_location,
) -> ([]byte, Allocator_Error) {
return nil, nil
@@ -41,7 +41,7 @@ not be allocated, and an attempt to allocate memory is an error.
panic_allocator :: proc() -> Allocator {
return Allocator{
procedure = panic_allocator_proc,
- data = nil,
+ data = nil,
}
}
@@ -157,10 +157,10 @@ This procedure returns a pointer to the newly allocated memory region.
*/
@(require_results)
arena_alloc :: proc(
- a: ^Arena,
+ a: ^Arena,
size: int,
alignment := DEFAULT_ALIGNMENT,
- loc := #caller_location,
+ loc := #caller_location,
) -> (rawptr, Allocator_Error) {
bytes, err := arena_alloc_bytes(a, size, alignment, loc)
return raw_data(bytes), err
@@ -175,10 +175,10 @@ This procedure returns a slice of the newly allocated memory region.
*/
@(require_results)
arena_alloc_bytes :: proc(
- a: ^Arena,
+ a: ^Arena,
size: int,
alignment := DEFAULT_ALIGNMENT,
- loc := #caller_location,
+ loc := #caller_location,
) -> ([]byte, Allocator_Error) {
bytes, err := arena_alloc_bytes_non_zeroed(a, size, alignment, loc)
if bytes != nil {
@@ -197,10 +197,10 @@ memory region.
*/
@(require_results)
arena_alloc_non_zeroed :: proc(
- a: ^Arena,
+ a: ^Arena,
size: int,
alignment := DEFAULT_ALIGNMENT,
- loc := #caller_location,
+ loc := #caller_location,
) -> (rawptr, Allocator_Error) {
bytes, err := arena_alloc_bytes_non_zeroed(a, size, alignment, loc)
return raw_data(bytes), err
@@ -216,10 +216,10 @@ memory region.
*/
@(require_results)
arena_alloc_bytes_non_zeroed :: proc(
- a: ^Arena,
+ a: ^Arena,
size: int,
alignment := DEFAULT_ALIGNMENT,
- loc := #caller_location
+ loc := #caller_location
) -> ([]byte, Allocator_Error) {
if a.data == nil {
panic("Arena is not initialized", loc)
@@ -244,11 +244,11 @@ arena_free_all :: proc(a: ^Arena) {
arena_allocator_proc :: proc(
allocator_data: rawptr,
- mode: Allocator_Mode,
- size: int,
- alignment: int,
- old_memory: rawptr,
- old_size: int,
+ mode: Allocator_Mode,
+ size: int,
+ alignment: int,
+ old_memory: rawptr,
+ old_size: int,
loc := #caller_location,
) -> ([]byte, Allocator_Error) {
arena := cast(^Arena)allocator_data
@@ -398,10 +398,10 @@ returns a pointer to the allocated memory region.
*/
@(require_results)
scratch_alloc :: proc(
- s: ^Scratch,
+ s: ^Scratch,
size: int,
alignment := DEFAULT_ALIGNMENT,
- loc := #caller_location,
+ loc := #caller_location,
) -> (rawptr, Allocator_Error) {
bytes, err := scratch_alloc_bytes(s, size, alignment, loc)
return raw_data(bytes), err
@@ -416,10 +416,10 @@ returns a slice of the allocated memory region.
*/
@(require_results)
scratch_alloc_bytes :: proc(
- s: ^Scratch,
+ s: ^Scratch,
size: int,
alignment := DEFAULT_ALIGNMENT,
- loc := #caller_location,
+ loc := #caller_location,
) -> ([]byte, Allocator_Error) {
bytes, err := scratch_alloc_bytes_non_zeroed(s, size, alignment, loc)
if bytes != nil {
@@ -437,10 +437,10 @@ This procedure returns a pointer to the allocated memory region.
*/
@(require_results)
scratch_alloc_non_zeroed :: proc(
- s: ^Scratch,
+ s: ^Scratch,
size: int,
alignment := DEFAULT_ALIGNMENT,
- loc := #caller_location,
+ loc := #caller_location,
) -> (rawptr, Allocator_Error) {
bytes, err := scratch_alloc_bytes_non_zeroed(s, size, alignment, loc)
return raw_data(bytes), err
@@ -455,10 +455,10 @@ This procedure returns a slice of the allocated memory region.
*/
@(require_results)
scratch_alloc_bytes_non_zeroed :: proc(
- s: ^Scratch,
+ s: ^Scratch,
size: int,
alignment := DEFAULT_ALIGNMENT,
- loc := #caller_location,
+ loc := #caller_location,
) -> ([]byte, Allocator_Error) {
if s.data == nil {
DEFAULT_BACKING_SIZE :: 4 * Megabyte
@@ -477,7 +477,7 @@ scratch_alloc_bytes_non_zeroed :: proc(
offset = 0
}
start := uintptr(raw_data(s.data))
- ptr := align_forward_uintptr(offset+start, uintptr(alignment))
+ ptr := align_forward_uintptr(offset+start, uintptr(alignment))
s.prev_allocation = rawptr(ptr)
s.curr_offset = int(offset) + size
return byte_slice(rawptr(ptr), size), nil
@@ -574,12 +574,12 @@ This procedure returns the pointer to the resized memory region.
*/
@(require_results)
scratch_resize :: proc(
- s: ^Scratch,
+ s: ^Scratch,
old_memory: rawptr,
- old_size: int,
- size: int,
+ old_size: int,
+ size: int,
alignment := DEFAULT_ALIGNMENT,
- loc := #caller_location
+ loc := #caller_location
) -> (rawptr, Allocator_Error) {
bytes, err := scratch_resize_bytes(s, byte_slice(old_memory, old_size), size, alignment, loc)
return raw_data(bytes), err
@@ -603,11 +603,11 @@ This procedure returns the slice of the resized memory region.
*/
@(require_results)
scratch_resize_bytes :: proc(
- s: ^Scratch,
+ s: ^Scratch,
old_data: []byte,
- size: int,
+ size: int,
alignment := DEFAULT_ALIGNMENT,
- loc := #caller_location
+ loc := #caller_location
) -> ([]byte, Allocator_Error) {
bytes, err := scratch_resize_bytes_non_zeroed(s, old_data, size, alignment, loc)
if bytes != nil && size > len(old_data) {
@@ -634,12 +634,12 @@ This procedure returns the pointer to the resized memory region.
*/
@(require_results)
scratch_resize_non_zeroed :: proc(
- s: ^Scratch,
+ s: ^Scratch,
old_memory: rawptr,
- old_size: int,
- size: int,
+ old_size: int,
+ size: int,
alignment := DEFAULT_ALIGNMENT,
- loc := #caller_location
+ loc := #caller_location
) -> (rawptr, Allocator_Error) {
bytes, err := scratch_resize_bytes_non_zeroed(s, byte_slice(old_memory, old_size), size, alignment, loc)
return raw_data(bytes), err
@@ -663,11 +663,11 @@ This procedure returns the slice of the resized memory region.
*/
@(require_results)
scratch_resize_bytes_non_zeroed :: proc(
- s: ^Scratch,
+ s: ^Scratch,
old_data: []byte,
- size: int,
+ size: int,
alignment := DEFAULT_ALIGNMENT,
- loc := #caller_location
+ loc := #caller_location
) -> ([]byte, Allocator_Error) {
old_memory := raw_data(old_data)
old_size := len(old_data)
@@ -678,8 +678,8 @@ scratch_resize_bytes_non_zeroed :: proc(
}
scratch_init(s, DEFAULT_BACKING_SIZE)
}
- begin := uintptr(raw_data(s.data))
- end := begin + uintptr(len(s.data))
+ begin := uintptr(raw_data(s.data))
+ end := begin + uintptr(len(s.data))
old_ptr := uintptr(old_memory)
if begin <= old_ptr && old_ptr < end && old_ptr+uintptr(size) < end {
s.curr_offset = int(old_ptr-begin)+size
@@ -695,11 +695,11 @@ scratch_resize_bytes_non_zeroed :: proc(
}
scratch_allocator_proc :: proc(
- allocator_data: rawptr,
- mode: Allocator_Mode,
+ allocator_data: rawptr,
+ mode: Allocator_Mode,
size, alignment: int,
- old_memory: rawptr,
- old_size: int,
+ old_memory: rawptr,
+ old_size: int,
loc := #caller_location,
) -> ([]byte, Allocator_Error) {
s := (^Scratch)(allocator_data)
@@ -735,10 +735,10 @@ scratch_allocator_proc :: proc(
Stack allocator data.
*/
Stack :: struct {
- data: []byte,
+ data: []byte,
prev_offset: int,
curr_offset: int,
- peak_used: int,
+ peak_used: int,
}
/*
@@ -769,7 +769,7 @@ previous allocation header.
stack_allocator :: proc(stack: ^Stack) -> Allocator {
return Allocator{
procedure = stack_allocator_proc,
- data = stack,
+ data = stack,
}
}
@@ -780,18 +780,18 @@ This procedure initializes the stack allocator with a backing buffer specified
by `data` parameter.
*/
stack_init :: proc(s: ^Stack, data: []byte) {
- s.data = data
+ s.data = data
s.prev_offset = 0
s.curr_offset = 0
- s.peak_used = 0
+ s.peak_used = 0
}
@(deprecated="prefer 'mem.stack_init'")
init_stack :: proc(s: ^Stack, data: []byte) {
- s.data = data
+ s.data = data
s.prev_offset = 0
s.curr_offset = 0
- s.peak_used = 0
+ s.peak_used = 0
}
/*
@@ -803,10 +803,10 @@ procedure returns the pointer to the allocated memory.
*/
@(require_results)
stack_alloc :: proc(
- s: ^Stack,
+ s: ^Stack,
size: int,
alignment := DEFAULT_ALIGNMENT,
- loc := #caller_location
+ loc := #caller_location
) -> (rawptr, Allocator_Error) {
bytes, err := stack_alloc_bytes(s, size, alignment, loc)
return raw_data(bytes), err
@@ -821,10 +821,10 @@ procedure returns the slice of the allocated memory.
*/
@(require_results)
stack_alloc_bytes :: proc(
- s: ^Stack,
+ s: ^Stack,
size: int,
alignment := DEFAULT_ALIGNMENT,
- loc := #caller_location
+ loc := #caller_location
) -> ([]byte, Allocator_Error) {
bytes, err := stack_alloc_bytes_non_zeroed(s, size, alignment, loc)
if bytes != nil {
@@ -842,10 +842,10 @@ zero-initialized. This procedure returns the pointer to the allocated memory.
*/
@(require_results)
stack_alloc_non_zeroed :: proc(
- s: ^Stack,
+ s: ^Stack,
size: int,
alignment := DEFAULT_ALIGNMENT,
- loc := #caller_location
+ loc := #caller_location
) -> (rawptr, Allocator_Error) {
bytes, err := stack_alloc_bytes_non_zeroed(s, size, alignment, loc)
return raw_data(bytes), err
@@ -860,10 +860,10 @@ zero-initialized. This procedure returns the slice of the allocated memory.
*/
@(require_results)
stack_alloc_bytes_non_zeroed :: proc(
- s: ^Stack,
+ s: ^Stack,
size: int,
alignment := DEFAULT_ALIGNMENT,
- loc := #caller_location
+ loc := #caller_location
) -> ([]byte, Allocator_Error) {
if s.data == nil {
panic("Stack allocation on an uninitialized stack allocator", loc)
@@ -896,7 +896,7 @@ If the freeing does is an out of order freeing, the `.Invalid_Pointer` error
is returned.
*/
stack_free :: proc(
- s: ^Stack,
+ s: ^Stack,
old_memory: rawptr,
loc := #caller_location,
) -> (Allocator_Error) {
@@ -953,12 +953,12 @@ This procedure returns the pointer to the resized memory region.
*/
@(require_results)
stack_resize :: proc(
- s: ^Stack,
+ s: ^Stack,
old_memory: rawptr,
- old_size: int,
- size: int,
+ old_size: int,
+ size: int,
alignment := DEFAULT_ALIGNMENT,
- loc := #caller_location,
+ loc := #caller_location,
) -> (rawptr, Allocator_Error) {
bytes, err := stack_resize_bytes(s, byte_slice(old_memory, old_size), size, alignment)
return raw_data(bytes), err
@@ -982,11 +982,11 @@ This procedure returns the slice of the resized memory region.
*/
@(require_results)
stack_resize_bytes :: proc(
- s: ^Stack,
+ s: ^Stack,
old_data: []byte,
- size: int,
+ size: int,
alignment := DEFAULT_ALIGNMENT,
- loc := #caller_location,
+ loc := #caller_location,
) -> ([]byte, Allocator_Error) {
bytes, err := stack_alloc_bytes_non_zeroed(s, size, alignment, loc)
if bytes != nil {
@@ -1017,12 +1017,12 @@ This procedure returns the pointer to the resized memory region.
*/
@(require_results)
stack_resize_non_zeroed :: proc(
- s: ^Stack,
+ s: ^Stack,
old_memory: rawptr,
- old_size: int,
- size: int,
+ old_size: int,
+ size: int,
alignment := DEFAULT_ALIGNMENT,
- loc := #caller_location,
+ loc := #caller_location,
) -> (rawptr, Allocator_Error) {
bytes, err := stack_resize_bytes_non_zeroed(s, byte_slice(old_memory, old_size), size, alignment)
return raw_data(bytes), err
@@ -1046,11 +1046,11 @@ This procedure returns the slice of the resized memory region.
*/
@(require_results)
stack_resize_bytes_non_zeroed :: proc(
- s: ^Stack,
+ s: ^Stack,
old_data: []byte,
- size: int,
+ size: int,
alignment := DEFAULT_ALIGNMENT,
- loc := #caller_location,
+ loc := #caller_location,
) -> ([]byte, Allocator_Error) {
old_memory := raw_data(old_data)
old_size := len(old_data)
@@ -1063,8 +1063,8 @@ stack_resize_bytes_non_zeroed :: proc(
if size == 0 {
return nil, nil
}
- start := uintptr(raw_data(s.data))
- end := start + uintptr(len(s.data))
+ start := uintptr(raw_data(s.data))
+ end := start + uintptr(len(s.data))
curr_addr := uintptr(old_memory)
if !(start <= curr_addr && curr_addr < end) {
panic("Out of bounds memory address passed to stack allocator (resize)")
@@ -1097,11 +1097,11 @@ stack_resize_bytes_non_zeroed :: proc(
stack_allocator_proc :: proc(
allocator_data: rawptr,
- mode: Allocator_Mode,
- size: int,
- alignment: int,
- old_memory: rawptr,
- old_size: int,
+ mode: Allocator_Mode,
+ size: int,
+ alignment: int,
+ old_memory: rawptr,
+ old_size: int,
loc := #caller_location,
) -> ([]byte, Allocator_Error) {
s := cast(^Stack)allocator_data
@@ -1200,10 +1200,10 @@ returns a pointer to the allocated memory region.
*/
@(require_results)
small_stack_alloc :: proc(
- s: ^Small_Stack,
+ s: ^Small_Stack,
size: int,
alignment := DEFAULT_ALIGNMENT,
- loc := #caller_location,
+ loc := #caller_location,
) -> (rawptr, Allocator_Error) {
bytes, err := small_stack_alloc_bytes(s, size, alignment, loc)
return raw_data(bytes), err
@@ -1218,10 +1218,10 @@ returns a slice of the allocated memory region.
*/
@(require_results)
small_stack_alloc_bytes :: proc(
- s: ^Small_Stack,
+ s: ^Small_Stack,
size: int,
alignment := DEFAULT_ALIGNMENT,
- loc := #caller_location,
+ loc := #caller_location,
) -> ([]byte, Allocator_Error) {
bytes, err := small_stack_alloc_bytes_non_zeroed(s, size, alignment, loc)
if bytes != nil {
@@ -1239,10 +1239,10 @@ procedure returns a pointer to the allocated memory region.
*/
@(require_results)
small_stack_alloc_non_zeroed :: proc(
- s: ^Small_Stack,
+ s: ^Small_Stack,
size: int,
alignment := DEFAULT_ALIGNMENT,
- loc := #caller_location,
+ loc := #caller_location,
) -> (rawptr, Allocator_Error) {
bytes, err := small_stack_alloc_bytes_non_zeroed(s, size, alignment, loc)
return raw_data(bytes), err
@@ -1257,10 +1257,10 @@ procedure returns a slice of the allocated memory region.
*/
@(require_results)
small_stack_alloc_bytes_non_zeroed :: proc(
- s: ^Small_Stack,
+ s: ^Small_Stack,
size: int,
alignment := DEFAULT_ALIGNMENT,
- loc := #caller_location,
+ loc := #caller_location,
) -> ([]byte, Allocator_Error) {
if s.data == nil {
panic("Small stack is not initialized", loc)
@@ -1289,7 +1289,7 @@ by `alignment`. The allocated memory is not explicitly zero-initialized. This
procedure returns a slice of the allocated memory region.
*/
small_stack_free :: proc(
- s: ^Small_Stack,
+ s: ^Small_Stack,
old_memory: rawptr,
loc := #caller_location,
) -> Allocator_Error {
@@ -1341,12 +1341,12 @@ This procedure returns the pointer to the resized memory region.
*/
@(require_results)
small_stack_resize :: proc(
- s: ^Small_Stack,
+ s: ^Small_Stack,
old_memory: rawptr,
- old_size: int,
- size: int,
+ old_size: int,
+ size: int,
alignment := DEFAULT_ALIGNMENT,
- loc := #caller_location,
+ loc := #caller_location,
) -> (rawptr, Allocator_Error) {
bytes, err := small_stack_resize_bytes(s, byte_slice(old_memory, old_size), size, alignment, loc)
return raw_data(bytes), err
@@ -1370,11 +1370,11 @@ This procedure returns the slice of the resized memory region.
*/
@(require_results)
small_stack_resize_bytes :: proc(
- s: ^Small_Stack,
+ s: ^Small_Stack,
old_data: []byte,
- size: int,
+ size: int,
alignment := DEFAULT_ALIGNMENT,
- loc := #caller_location,
+ loc := #caller_location,
) -> ([]byte, Allocator_Error) {
bytes, err := small_stack_resize_bytes_non_zeroed(s, old_data, size, alignment, loc)
if bytes != nil {
@@ -1405,12 +1405,12 @@ This procedure returns the pointer to the resized memory region.
*/
@(require_results)
small_stack_resize_non_zeroed :: proc(
- s: ^Small_Stack,
+ s: ^Small_Stack,
old_memory: rawptr,
- old_size: int,
- size: int,
+ old_size: int,
+ size: int,
alignment := DEFAULT_ALIGNMENT,
- loc := #caller_location,
+ loc := #caller_location,
) -> (rawptr, Allocator_Error) {
bytes, err := small_stack_resize_bytes_non_zeroed(s, byte_slice(old_memory, old_size), size, alignment, loc)
return raw_data(bytes), err
@@ -1434,18 +1434,18 @@ This procedure returns the slice of the resized memory region.
*/
@(require_results)
small_stack_resize_bytes_non_zeroed :: proc(
- s: ^Small_Stack,
+ s: ^Small_Stack,
old_data: []byte,
- size: int,
+ size: int,
alignment := DEFAULT_ALIGNMENT,
- loc := #caller_location,
+ loc := #caller_location,
) -> ([]byte, Allocator_Error) {
if s.data == nil {
panic("Small stack is not initialized", loc)
}
old_memory := raw_data(old_data)
- old_size := len(old_data)
- alignment := alignment
+ old_size := len(old_data)
+ alignment := alignment
alignment = clamp(alignment, 1, 8*size_of(Stack_Allocation_Header{}.padding)/2)
if old_memory == nil {
return small_stack_alloc_bytes_non_zeroed(s, size, alignment, loc)
@@ -1453,8 +1453,8 @@ small_stack_resize_bytes_non_zeroed :: proc(
if size == 0 {
return nil, nil
}
- start := uintptr(raw_data(s.data))
- end := start + uintptr(len(s.data))
+ start := uintptr(raw_data(s.data))
+ end := start + uintptr(len(s.data))
curr_addr := uintptr(old_memory)
if !(start <= curr_addr && curr_addr < end) {
// panic("Out of bounds memory address passed to stack allocator (resize)");
@@ -1476,11 +1476,11 @@ small_stack_resize_bytes_non_zeroed :: proc(
}
small_stack_allocator_proc :: proc(
- allocator_data: rawptr,
- mode: Allocator_Mode,
- size, alignment: int,
- old_memory: rawptr,
- old_size: int,
+ allocator_data: rawptr,
+ mode: Allocator_Mode,
+ size, alignment: int,
+ old_memory: rawptr,
+ old_size: int,
loc := #caller_location,
) -> ([]byte, Allocator_Error) {
s := cast(^Small_Stack)allocator_data
@@ -1514,17 +1514,17 @@ small_stack_allocator_proc :: proc(
/* Preserved for compatibility */
-Dynamic_Pool :: Dynamic_Arena
-DYNAMIC_POOL_BLOCK_SIZE_DEFAULT :: DYNAMIC_ARENA_BLOCK_SIZE_DEFAULT
+Dynamic_Pool :: Dynamic_Arena
+DYNAMIC_POOL_BLOCK_SIZE_DEFAULT :: DYNAMIC_ARENA_BLOCK_SIZE_DEFAULT
DYNAMIC_POOL_OUT_OF_BAND_SIZE_DEFAULT :: DYNAMIC_ARENA_OUT_OF_BAND_SIZE_DEFAULT
-dynamic_pool_allocator_proc :: dynamic_arena_allocator_proc
-dynamic_pool_free_all :: dynamic_arena_free_all
-dynamic_pool_reset :: dynamic_arena_reset
-dynamic_pool_alloc_bytes :: dynamic_arena_alloc_bytes
-dynamic_pool_alloc :: dynamic_arena_alloc
-dynamic_pool_init :: dynamic_arena_init
-dynamic_pool_allocator :: dynamic_arena_allocator
-dynamic_pool_destroy :: dynamic_arena_destroy
+dynamic_pool_allocator_proc :: dynamic_arena_allocator_proc
+dynamic_pool_free_all :: dynamic_arena_free_all
+dynamic_pool_reset :: dynamic_arena_reset
+dynamic_pool_alloc_bytes :: dynamic_arena_alloc_bytes
+dynamic_pool_alloc :: dynamic_arena_alloc
+dynamic_pool_init :: dynamic_arena_init
+dynamic_pool_allocator :: dynamic_arena_allocator
+dynamic_pool_destroy :: dynamic_arena_destroy
/*
Default block size for dynamic arena.
@@ -1540,16 +1540,16 @@ DYNAMIC_ARENA_OUT_OF_BAND_SIZE_DEFAULT :: 6554
Dynamic arena allocator data.
*/
Dynamic_Arena :: struct {
- block_size: int,
- out_band_size: int,
- alignment: int,
- unused_blocks: [dynamic]rawptr,
- used_blocks: [dynamic]rawptr,
+ block_size: int,
+ out_band_size: int,
+ alignment: int,
+ unused_blocks: [dynamic]rawptr,
+ used_blocks: [dynamic]rawptr,
out_band_allocations: [dynamic]rawptr,
- current_block: rawptr,
- current_pos: rawptr,
- bytes_left: int,
- block_allocator: Allocator,
+ current_block: rawptr,
+ current_pos: rawptr,
+ bytes_left: int,
+ block_allocator: Allocator,
}
/*
@@ -1565,17 +1565,17 @@ dynamic_arena_init :: proc(
pool: ^Dynamic_Arena,
block_allocator := context.allocator,
array_allocator := context.allocator,
- block_size := DYNAMIC_ARENA_BLOCK_SIZE_DEFAULT,
- out_band_size := DYNAMIC_ARENA_OUT_OF_BAND_SIZE_DEFAULT,
- alignment := DEFAULT_ALIGNMENT,
+ block_size := DYNAMIC_ARENA_BLOCK_SIZE_DEFAULT,
+ out_band_size := DYNAMIC_ARENA_OUT_OF_BAND_SIZE_DEFAULT,
+ alignment := DEFAULT_ALIGNMENT,
) {
- pool.block_size = block_size
- pool.out_band_size = out_band_size
- pool.alignment = alignment
- pool.block_allocator = block_allocator
+ pool.block_size = block_size
+ pool.out_band_size = out_band_size
+ pool.alignment = alignment
+ pool.block_allocator = block_allocator
pool.out_band_allocations.allocator = array_allocator
- pool.unused_blocks.allocator = array_allocator
- pool.used_blocks.allocator = array_allocator
+ pool.unused_blocks.allocator = array_allocator
+ pool.used_blocks.allocator = array_allocator
}
/*
@@ -1783,10 +1783,10 @@ This procedure returns the pointer to the resized memory region.
*/
@(require_results)
dynamic_arena_resize :: proc(
- a: ^Dynamic_Arena,
+ a: ^Dynamic_Arena,
old_memory: rawptr,
- old_size: int,
- size: int,
+ old_size: int,
+ size: int,
loc := #caller_location,
) -> (rawptr, Allocator_Error) {
bytes, err := dynamic_arena_resize_bytes(a, byte_slice(old_memory, old_size), size, loc)
@@ -1811,9 +1811,9 @@ This procedure returns the slice of the resized memory region.
*/
@(require_results)
dynamic_arena_resize_bytes :: proc(
- a: ^Dynamic_Arena,
+ a: ^Dynamic_Arena,
old_data: []byte,
- size: int,
+ size: int,
loc := #caller_location,
) -> ([]byte, Allocator_Error) {
bytes, err := dynamic_arena_resize_bytes_non_zeroed(a, old_data, size, loc)
@@ -1845,10 +1845,10 @@ This procedure returns the pointer to the resized memory region.
*/
@(require_results)
dynamic_arena_resize_non_zeroed :: proc(
- a: ^Dynamic_Arena,
+ a: ^Dynamic_Arena,
old_memory: rawptr,
- old_size: int,
- size: int,
+ old_size: int,
+ size: int,
loc := #caller_location,
) -> (rawptr, Allocator_Error) {
bytes, err := dynamic_arena_resize_bytes_non_zeroed(a, byte_slice(old_memory, old_size), size, loc)
@@ -1873,9 +1873,9 @@ This procedure returns the slice of the resized memory region.
*/
@(require_results)
dynamic_arena_resize_bytes_non_zeroed :: proc(
- a: ^Dynamic_Arena,
+ a: ^Dynamic_Arena,
old_data: []byte,
- size: int,
+ size: int,
loc := #caller_location,
) -> ([]byte, Allocator_Error) {
old_memory := raw_data(old_data)
@@ -1892,11 +1892,11 @@ dynamic_arena_resize_bytes_non_zeroed :: proc(
dynamic_arena_allocator_proc :: proc(
allocator_data: rawptr,
- mode: Allocator_Mode,
- size: int,
- alignment: int,
- old_memory: rawptr,
- old_size: int,
+ mode: Allocator_Mode,
+ size: int,
+ alignment: int,
+ old_memory: rawptr,
+ old_size: int,
loc := #caller_location,
) -> ([]byte, Allocator_Error) {
arena := (^Dynamic_Arena)(allocator_data)
@@ -2048,7 +2048,7 @@ buddy_block_find_best :: proc(head, tail: ^Buddy_Block, size: uint) -> ^Buddy_Bl
// to pick the buddy as it "bounces around" less
best_block = buddy
}
- if (block.size <= buddy.size) {
+ if block.size <= buddy.size {
block = buddy_block_next(buddy)
if (block < tail) {
// Delay the buddy block for the next iteration
@@ -2072,8 +2072,8 @@ buddy_block_find_best :: proc(head, tail: ^Buddy_Block, size: uint) -> ^Buddy_Bl
The buddy allocator data.
*/
Buddy_Allocator :: struct {
- head: ^Buddy_Block,
- tail: ^Buddy_Block,
+ head: ^Buddy_Block,
+ tail: ^Buddy_Block,
alignment: uint,
}
@@ -2234,11 +2234,11 @@ buddy_allocator_free_all :: proc(b: ^Buddy_Allocator) {
}
buddy_allocator_proc :: proc(
- allocator_data: rawptr,
- mode: Allocator_Mode,
+ allocator_data: rawptr,
+ mode: Allocator_Mode,
size, alignment: int,
- old_memory: rawptr,
- old_size: int,
+ old_memory: rawptr,
+ old_size: int,
loc := #caller_location,
) -> ([]byte, Allocator_Error) {
b := (^Buddy_Allocator)(allocator_data)
diff --git a/core/mem/mem.odin b/core/mem/mem.odin
index 67ed56c39..ccbc77798 100644
--- a/core/mem/mem.odin
+++ b/core/mem/mem.odin
@@ -461,10 +461,12 @@ Check if a pointer is aligned.
This procedure checks whether a pointer `x` is aligned to a boundary specified
by `align`, and returns `true` if the pointer is aligned, and false otherwise.
+
+The specified alignment must be a power of 2.
*/
is_aligned :: proc "contextless" (x: rawptr, align: int) -> bool {
p := uintptr(x)
- return (p & (1<<uintptr(align) - 1)) == 0
+ return (p & (uintptr(align) - 1)) == 0
}
/*
diff --git a/core/mem/mutex_allocator.odin b/core/mem/mutex_allocator.odin
index b8062bca1..7361016c3 100644
--- a/core/mem/mutex_allocator.odin
+++ b/core/mem/mutex_allocator.odin
@@ -1,4 +1,4 @@
-#+build !freestanding
+#+build !freestanding, wasm32, wasm64p32
package mem
import "core:sync"
diff --git a/core/mem/tracking_allocator.odin b/core/mem/tracking_allocator.odin
index 5da38e62f..cf780de3f 100644
--- a/core/mem/tracking_allocator.odin
+++ b/core/mem/tracking_allocator.odin
@@ -1,4 +1,4 @@
-#+build !freestanding
+#+build !freestanding, wasm32, wasm64p32
package mem
import "base:runtime"
diff --git a/core/mem/virtual/virtual_posix.odin b/core/mem/virtual/virtual_posix.odin
index 105849774..c3d6a9095 100644
--- a/core/mem/virtual/virtual_posix.odin
+++ b/core/mem/virtual/virtual_posix.odin
@@ -6,17 +6,13 @@ import "core:sys/posix"
// Define non-posix needed flags:
when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD {
- MAP_ANONYMOUS :: 0x1000 /* allocated from memory, swap space */
-
- MADV_FREE :: 5 /* pages unneeded, discard contents */
+ MADV_FREE :: 5 /* pages unneeded, discard contents */
} else when ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD {
- MAP_ANONYMOUS :: 0x1000
-
- MADV_FREE :: 6
+ MADV_FREE :: 6
}
_reserve :: proc "contextless" (size: uint) -> (data: []byte, err: Allocator_Error) {
- flags := posix.Map_Flags{ .PRIVATE } + transmute(posix.Map_Flags)i32(MAP_ANONYMOUS)
+ flags := posix.Map_Flags{ .ANONYMOUS, .PRIVATE }
result := posix.mmap(nil, size, {}, flags)
if result == posix.MAP_FAILED {
return nil, .Out_Of_Memory
diff --git a/core/net/socket_darwin.odin b/core/net/socket_darwin.odin
index ec9255c3b..27927e973 100644
--- a/core/net/socket_darwin.odin
+++ b/core/net/socket_darwin.odin
@@ -88,8 +88,8 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
sockaddr := _endpoint_to_sockaddr(endpoint)
res := os.connect(os.Socket(skt), (^os.SOCKADDR)(&sockaddr), i32(sockaddr.len))
if res != nil {
- err = Dial_Error(os.is_platform_error(res) or_else -1)
- return
+ close(skt)
+ return {}, Dial_Error(os.is_platform_error(res) or_else -1)
}
return
@@ -120,6 +120,7 @@ _listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (skt: TCP_
family := family_from_endpoint(interface_endpoint)
sock := create_socket(family, .TCP) or_return
skt = sock.(TCP_Socket)
+ defer if err != nil { close(skt) }
// NOTE(tetra): This is so that if we crash while the socket is open, we can
// bypass the cooldown period, and allow the next run of the program to
diff --git a/core/net/socket_freebsd.odin b/core/net/socket_freebsd.odin
index 0f3a85cbb..3a3774007 100644
--- a/core/net/socket_freebsd.odin
+++ b/core/net/socket_freebsd.odin
@@ -114,8 +114,8 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
sockaddr := _endpoint_to_sockaddr(endpoint)
errno := freebsd.connect(cast(Fd)socket, &sockaddr, cast(freebsd.socklen_t)sockaddr.len)
if errno != nil {
- err = cast(Dial_Error)errno
- return
+ close(socket)
+ return {}, cast(Dial_Error)errno
}
return
@@ -137,6 +137,7 @@ _listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (socket: T
family := family_from_endpoint(interface_endpoint)
new_socket := create_socket(family, .TCP) or_return
socket = new_socket.(TCP_Socket)
+ defer if err != nil { close(socket) }
bind(socket, interface_endpoint) or_return
diff --git a/core/net/socket_linux.odin b/core/net/socket_linux.odin
index a3853874a..b7816b0b6 100644
--- a/core/net/socket_linux.odin
+++ b/core/net/socket_linux.odin
@@ -147,7 +147,8 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
addr := _unwrap_os_addr(endpoint)
errno = linux.connect(linux.Fd(os_sock), &addr)
if errno != .NONE {
- return cast(TCP_Socket) os_sock, Dial_Error(errno)
+ close(cast(TCP_Socket) os_sock)
+ return {}, Dial_Error(errno)
}
// NOTE(tetra): Not vital to succeed; error ignored
no_delay: b32 = cast(b32) options.no_delay
@@ -166,40 +167,48 @@ _bind :: proc(sock: Any_Socket, endpoint: Endpoint) -> (Network_Error) {
}
@(private)
-_listen_tcp :: proc(endpoint: Endpoint, backlog := 1000) -> (TCP_Socket, Network_Error) {
+_listen_tcp :: proc(endpoint: Endpoint, backlog := 1000) -> (socket: TCP_Socket, err: Network_Error) {
errno: linux.Errno
assert(backlog > 0 && i32(backlog) < max(i32))
+
// Figure out the address family and address of the endpoint
ep_family := _unwrap_os_family(family_from_endpoint(endpoint))
ep_address := _unwrap_os_addr(endpoint)
+
// Create TCP socket
os_sock: linux.Fd
os_sock, errno = linux.socket(ep_family, .STREAM, {.CLOEXEC}, .TCP)
if errno != .NONE {
- // TODO(flysand): should return invalid file descriptor here casted as TCP_Socket
- return {}, Create_Socket_Error(errno)
+ err = Create_Socket_Error(errno)
+ return
}
+ socket = cast(TCP_Socket)os_sock
+ defer if err != nil { close(socket) }
+
// NOTE(tetra): This is so that if we crash while the socket is open, we can
// bypass the cooldown period, and allow the next run of the program to
// use the same address immediately.
//
// TODO(tetra, 2022-02-15): Confirm that this doesn't mean other processes can hijack the address!
do_reuse_addr: b32 = true
- errno = linux.setsockopt(os_sock, linux.SOL_SOCKET, linux.Socket_Option.REUSEADDR, &do_reuse_addr)
- if errno != .NONE {
- return cast(TCP_Socket) os_sock, Listen_Error(errno)
+ if errno = linux.setsockopt(os_sock, linux.SOL_SOCKET, linux.Socket_Option.REUSEADDR, &do_reuse_addr); errno != .NONE {
+ err = Listen_Error(errno)
+ return
}
+
// Bind the socket to endpoint address
- errno = linux.bind(os_sock, &ep_address)
- if errno != .NONE {
- return cast(TCP_Socket) os_sock, Bind_Error(errno)
+ if errno = linux.bind(os_sock, &ep_address); errno != .NONE {
+ err = Bind_Error(errno)
+ return
}
+
// Listen on bound socket
- errno = linux.listen(os_sock, cast(i32) backlog)
- if errno != .NONE {
- return cast(TCP_Socket) os_sock, Listen_Error(errno)
+ if errno = linux.listen(os_sock, cast(i32) backlog); errno != .NONE {
+ err = Listen_Error(errno)
+ return
}
- return cast(TCP_Socket) os_sock, nil
+
+ return
}
@(private)
diff --git a/core/net/socket_windows.odin b/core/net/socket_windows.odin
index 20f17619d..747d5cab3 100644
--- a/core/net/socket_windows.odin
+++ b/core/net/socket_windows.odin
@@ -80,8 +80,8 @@ _dial_tcp_from_endpoint :: proc(endpoint: Endpoint, options := default_tcp_optio
sockaddr := _endpoint_to_sockaddr(endpoint)
res := win.connect(win.SOCKET(socket), &sockaddr, size_of(sockaddr))
if res < 0 {
- err = Dial_Error(win.WSAGetLastError())
- return
+ close(socket)
+ return {}, Dial_Error(win.WSAGetLastError())
}
if options.no_delay {
@@ -107,6 +107,7 @@ _listen_tcp :: proc(interface_endpoint: Endpoint, backlog := 1000) -> (socket: T
family := family_from_endpoint(interface_endpoint)
sock := create_socket(family, .TCP) or_return
socket = sock.(TCP_Socket)
+ defer if err != nil { close(socket) }
// NOTE(tetra): While I'm not 100% clear on it, my understanding is that this will
// prevent hijacking of the server's endpoint by other applications.
diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin
index d67eb31f3..f62feec8c 100644
--- a/core/odin/ast/ast.odin
+++ b/core/odin/ast/ast.odin
@@ -776,17 +776,18 @@ Dynamic_Array_Type :: struct {
Struct_Type :: struct {
using node: Expr,
- tok_pos: tokenizer.Pos,
- poly_params: ^Field_List,
- align: ^Expr,
- field_align: ^Expr,
- where_token: tokenizer.Token,
- where_clauses: []^Expr,
- is_packed: bool,
- is_raw_union: bool,
- is_no_copy: bool,
- fields: ^Field_List,
- name_count: int,
+ tok_pos: tokenizer.Pos,
+ poly_params: ^Field_List,
+ align: ^Expr,
+ min_field_align: ^Expr,
+ max_field_align: ^Expr,
+ where_token: tokenizer.Token,
+ where_clauses: []^Expr,
+ is_packed: bool,
+ is_raw_union: bool,
+ is_no_copy: bool,
+ fields: ^Field_List,
+ name_count: int,
}
Union_Type_Kind :: enum u8 {
diff --git a/core/odin/ast/clone.odin b/core/odin/ast/clone.odin
index b0a1673b2..67f7ffa95 100644
--- a/core/odin/ast/clone.odin
+++ b/core/odin/ast/clone.odin
@@ -316,7 +316,8 @@ clone_node :: proc(node: ^Node) -> ^Node {
case ^Struct_Type:
r.poly_params = auto_cast clone(r.poly_params)
r.align = clone(r.align)
- r.field_align = clone(r.field_align)
+ r.min_field_align = clone(r.min_field_align)
+ r.max_field_align = clone(r.max_field_align)
r.fields = auto_cast clone(r.fields)
case ^Union_Type:
r.poly_params = auto_cast clone(r.poly_params)
diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin
index d42766bde..5a7440339 100644
--- a/core/odin/parser/parser.odin
+++ b/core/odin/parser/parser.odin
@@ -2610,9 +2610,10 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
case .Struct:
tok := expect_token(p, .Struct)
- poly_params: ^ast.Field_List
- align: ^ast.Expr
- field_align: ^ast.Expr
+ poly_params: ^ast.Field_List
+ align: ^ast.Expr
+ min_field_align: ^ast.Expr
+ max_field_align: ^ast.Expr
is_packed: bool
is_raw_union: bool
is_no_copy: bool
@@ -2645,10 +2646,21 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
}
align = parse_expr(p, true)
case "field_align":
- if field_align != nil {
+ if min_field_align != nil {
+ error(p, tag.pos, "duplicate struct tag '#%s'", tag.text)
+ }
+ warn(p, tag.pos, "#field_align has been deprecated in favour of #min_field_align")
+ min_field_align = parse_expr(p, true)
+ case "min_field_align":
+ if min_field_align != nil {
error(p, tag.pos, "duplicate struct tag '#%s'", tag.text)
}
- field_align = parse_expr(p, true)
+ min_field_align = parse_expr(p, true)
+ case "max_field_align":
+ if max_field_align != nil {
+ error(p, tag.pos, "duplicate struct tag '#%s'", tag.text)
+ }
+ max_field_align = parse_expr(p, true)
case "raw_union":
if is_raw_union {
error(p, tag.pos, "duplicate struct tag '#%s'", tag.text)
@@ -2689,16 +2701,17 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
close := expect_closing_brace_of_field_list(p)
st := ast.new(ast.Struct_Type, tok.pos, end_pos(close))
- st.poly_params = poly_params
- st.align = align
- st.field_align = field_align
- st.is_packed = is_packed
- st.is_raw_union = is_raw_union
- st.is_no_copy = is_no_copy
- st.fields = fields
- st.name_count = name_count
- st.where_token = where_token
- st.where_clauses = where_clauses
+ st.poly_params = poly_params
+ st.align = align
+ st.min_field_align = min_field_align
+ st.max_field_align = max_field_align
+ st.is_packed = is_packed
+ st.is_raw_union = is_raw_union
+ st.is_no_copy = is_no_copy
+ st.fields = fields
+ st.name_count = name_count
+ st.where_token = where_token
+ st.where_clauses = where_clauses
return st
case .Union:
@@ -3683,6 +3696,8 @@ parse_value_decl :: proc(p: ^Parser, names: []^ast.Expr, docs: ^ast.Comment_Grou
}
}
+ end := p.prev_tok
+
if p.expr_level >= 0 {
end: ^ast.Expr
if !is_mutable && len(values) > 0 {
@@ -3702,7 +3717,7 @@ parse_value_decl :: proc(p: ^Parser, names: []^ast.Expr, docs: ^ast.Comment_Grou
}
}
- decl := ast.new(ast.Value_Decl, names[0].pos, end_pos(p.prev_tok))
+ decl := ast.new(ast.Value_Decl, names[0].pos, end_pos(end))
decl.docs = docs
decl.names = names
decl.type = type
diff --git a/core/odin/tokenizer/tokenizer.odin b/core/odin/tokenizer/tokenizer.odin
index c3a30581c..d4da82c56 100644
--- a/core/odin/tokenizer/tokenizer.odin
+++ b/core/odin/tokenizer/tokenizer.odin
@@ -331,7 +331,7 @@ scan_escape :: proc(t: ^Tokenizer) -> bool {
n -= 1
}
- if x > max || 0xd800 <= x && x <= 0xe000 {
+ if x > max || 0xd800 <= x && x <= 0xdfff {
error(t, offset, "escape sequence is an invalid Unicode code point")
return false
}
diff --git a/core/os/os2/allocators.odin b/core/os/os2/allocators.odin
index a205cae48..864532850 100644
--- a/core/os/os2/allocators.odin
+++ b/core/os/os2/allocators.odin
@@ -62,8 +62,8 @@ TEMP_ALLOCATOR_GUARD_END :: proc(temp: runtime.Arena_Temp, loc := #caller_locati
@(deferred_out=TEMP_ALLOCATOR_GUARD_END)
TEMP_ALLOCATOR_GUARD :: #force_inline proc(loc := #caller_location) -> (runtime.Arena_Temp, runtime.Source_Code_Location) {
- tmp := temp_allocator_temp_begin(loc)
global_default_temp_allocator_index = (global_default_temp_allocator_index+1)%MAX_TEMP_ARENA_COUNT
+ tmp := temp_allocator_temp_begin(loc)
return tmp, loc
}
diff --git a/core/os/os2/dir_linux.odin b/core/os/os2/dir_linux.odin
index 6a097e192..f26b4fc79 100644
--- a/core/os/os2/dir_linux.odin
+++ b/core/os/os2/dir_linux.odin
@@ -13,7 +13,7 @@ _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info
@(require_results)
_read_directory_iterator_create :: proc(f: ^File) -> (Read_Directory_Iterator, Error) {
- return {}, nil
+ return {}, .Unsupported
}
_read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
diff --git a/core/os/os2/dir_windows.odin b/core/os/os2/dir_windows.odin
index 09990aeec..f71e7e763 100644
--- a/core/os/os2/dir_windows.odin
+++ b/core/os/os2/dir_windows.odin
@@ -16,28 +16,25 @@ find_data_to_file_info :: proc(base_path: string, d: ^win32.WIN32_FIND_DATAW, al
}
path := concatenate({base_path, `\`, win32_utf16_to_utf8(d.cFileName[:], temp_allocator()) or_else ""}, allocator) or_return
+ handle := win32.HANDLE(_open_internal(path, {.Read}, 0o666) or_else 0)
+ defer win32.CloseHandle(handle)
fi.fullpath = path
fi.name = basename(path)
fi.size = i64(d.nFileSizeHigh)<<32 + i64(d.nFileSizeLow)
- fi.type, fi.mode = _file_type_mode_from_file_attributes(d.dwFileAttributes, nil, d.dwReserved0)
+ fi.type, fi.mode = _file_type_mode_from_file_attributes(d.dwFileAttributes, handle, d.dwReserved0)
fi.creation_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftCreationTime))
fi.modification_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastWriteTime))
fi.access_time = time.unix(0, win32.FILETIME_as_unix_nanoseconds(d.ftLastAccessTime))
-
- handle := win32.HANDLE(_open_internal(path, {.Read}, 0o666) or_else 0)
- defer win32.CloseHandle(handle)
-
if file_id_info: win32.FILE_ID_INFO; handle != nil && win32.GetFileInformationByHandleEx(handle, .FileIdInfo, &file_id_info, size_of(file_id_info)) {
#assert(size_of(fi.inode) == size_of(file_id_info.FileId))
#assert(size_of(fi.inode) == 16)
runtime.mem_copy_non_overlapping(&fi.inode, &file_id_info.FileId, 16)
}
-
return
}
@@ -137,5 +134,6 @@ _read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
return
}
file_info_delete(it.impl.prev_fi, file_allocator())
+ delete(it.impl.path, file_allocator())
win32.FindClose(it.impl.find_handle)
}
diff --git a/core/os/os2/errors_linux.odin b/core/os/os2/errors_linux.odin
index 09492110d..a7556c306 100644
--- a/core/os/os2/errors_linux.odin
+++ b/core/os/os2/errors_linux.odin
@@ -162,6 +162,8 @@ _get_platform_error :: proc(errno: linux.Errno) -> Error {
return .Invalid_File
case .ENOMEM:
return .Out_Of_Memory
+ case .ENOSYS:
+ return .Unsupported
}
return Platform_Error(i32(errno))
diff --git a/core/os/os2/errors_posix.odin b/core/os/os2/errors_posix.odin
index 59f0ba5f1..0b5876c0b 100644
--- a/core/os/os2/errors_posix.odin
+++ b/core/os/os2/errors_posix.odin
@@ -26,6 +26,8 @@ _get_platform_error :: proc() -> Error {
return .Invalid_File
case .ENOMEM:
return .Out_Of_Memory
+ case .ENOSYS:
+ return .Unsupported
case:
return Platform_Error(errno)
}
diff --git a/core/os/os2/errors_windows.odin b/core/os/os2/errors_windows.odin
index 56acd503f..404560f98 100644
--- a/core/os/os2/errors_windows.odin
+++ b/core/os/os2/errors_windows.odin
@@ -55,13 +55,15 @@ _get_platform_error :: proc() -> Error {
case win32.ERROR_NEGATIVE_SEEK:
return .Invalid_Offset
+ case win32.ERROR_BROKEN_PIPE:
+ return .Broken_Pipe
+
case
win32.ERROR_BAD_ARGUMENTS,
win32.ERROR_INVALID_PARAMETER,
win32.ERROR_NOT_ENOUGH_MEMORY,
win32.ERROR_NO_MORE_FILES,
win32.ERROR_LOCK_VIOLATION,
- win32.ERROR_BROKEN_PIPE,
win32.ERROR_CALL_NOT_IMPLEMENTED,
win32.ERROR_INSUFFICIENT_BUFFER,
win32.ERROR_INVALID_NAME,
diff --git a/core/os/os2/file_util.odin b/core/os/os2/file_util.odin
index 963544985..8af46fab3 100644
--- a/core/os/os2/file_util.odin
+++ b/core/os/os2/file_util.odin
@@ -164,7 +164,7 @@ read_entire_file_from_file :: proc(f: ^File, allocator: runtime.Allocator) -> (d
}
@(require_results)
-write_entire_file :: proc(name: string, data: []byte, perm: int, truncate := true) -> Error {
+write_entire_file :: proc(name: string, data: []byte, perm: int = 0o644, truncate := true) -> Error {
flags := O_WRONLY|O_CREATE
if truncate {
flags |= O_TRUNC
diff --git a/core/os/os2/file_windows.odin b/core/os/os2/file_windows.odin
index 511935d74..b91a1bc3b 100644
--- a/core/os/os2/file_windows.odin
+++ b/core/os/os2/file_windows.odin
@@ -50,9 +50,9 @@ init_std_files :: proc() {
}
@(fini)
fini_std_files :: proc() {
- close(stdin)
- close(stdout)
- close(stderr)
+ _destroy((^File_Impl)(stdin.impl))
+ _destroy((^File_Impl)(stdout.impl))
+ _destroy((^File_Impl)(stderr.impl))
}
diff --git a/core/os/os2/path_posix.odin b/core/os/os2/path_posix.odin
index 6f358c58d..5ffdac28e 100644
--- a/core/os/os2/path_posix.odin
+++ b/core/os/os2/path_posix.odin
@@ -40,7 +40,7 @@ _mkdir_all :: proc(path: string, perm: int) -> Error {
internal_mkdir_all :: proc(path: string, perm: int) -> Error {
dir, file := filepath.split(path)
- if file != path {
+ if file != path && dir != "/" {
if len(dir) > 1 && dir[len(dir) - 1] == '/' {
dir = dir[:len(dir) - 1]
}
diff --git a/core/os/os2/pipe.odin b/core/os/os2/pipe.odin
index 9254d6f8e..5d3e8368e 100644
--- a/core/os/os2/pipe.odin
+++ b/core/os/os2/pipe.odin
@@ -1,6 +1,43 @@
package os2
+/*
+Create an anonymous pipe.
+
+This procedure creates an anonymous pipe, returning two ends of the pipe, `r`
+and `w`. The file `r` is the readable end of the pipe. The file `w` is a
+writeable end of the pipe.
+
+Pipes are used as an inter-process communication mechanism, to communicate
+between a parent and a child process. The child uses one end of the pipe to
+write data, and the parent uses the other end to read from the pipe
+(or vice-versa). When a parent passes one of the ends of the pipe to the child
+process, that end of the pipe needs to be closed by the parent, before any data
+is attempted to be read.
+
+Although pipes look like files and is compatible with most file APIs in package
+os2, the way it's meant to be read is different. Due to asynchronous nature of
+the communication channel, the data may not be present at the time of a read
+request. The other scenario is when a pipe has no data because the other end
+of the pipe was closed by the child process.
+*/
@(require_results)
pipe :: proc() -> (r, w: ^File, err: Error) {
return _pipe()
}
+
+/*
+Check if the pipe has any data.
+
+This procedure checks whether a read-end of the pipe has data that can be
+read, and returns `true`, if the pipe has readable data, and `false` if the
+pipe is empty. This procedure does not block the execution of the current
+thread.
+
+**Note**: If the other end of the pipe was closed by the child process, the
+`.Broken_Pipe`
+can be returned by this procedure. Handle these errors accordingly.
+*/
+@(require_results)
+pipe_has_data :: proc(r: ^File) -> (ok: bool, err: Error) {
+ return _pipe_has_data(r)
+}
diff --git a/core/os/os2/pipe_linux.odin b/core/os/os2/pipe_linux.odin
index ac3382bc3..852674c69 100644
--- a/core/os/os2/pipe_linux.odin
+++ b/core/os/os2/pipe_linux.odin
@@ -15,3 +15,29 @@ _pipe :: proc() -> (r, w: ^File, err: Error) {
return
}
+
+@(require_results)
+_pipe_has_data :: proc(r: ^File) -> (ok: bool, err: Error) {
+ if r == nil || r.impl == nil {
+ return false, nil
+ }
+ fd := linux.Fd((^File_Impl)(r.impl).fd)
+ poll_fds := []linux.Poll_Fd {
+ linux.Poll_Fd {
+ fd = fd,
+ events = {.IN, .HUP},
+ },
+ }
+ n, errno := linux.poll(poll_fds, 0)
+ if n != 1 || errno != nil {
+ return false, _get_platform_error(errno)
+ }
+ pipe_events := poll_fds[0].revents
+ if pipe_events >= {.IN} {
+ return true, nil
+ }
+ if pipe_events >= {.HUP} {
+ return false, .Broken_Pipe
+ }
+ return false, nil
+} \ No newline at end of file
diff --git a/core/os/os2/pipe_posix.odin b/core/os/os2/pipe_posix.odin
index 487e32aea..df9425339 100644
--- a/core/os/os2/pipe_posix.odin
+++ b/core/os/os2/pipe_posix.odin
@@ -44,3 +44,30 @@ _pipe :: proc() -> (r, w: ^File, err: Error) {
return
}
+@(require_results)
+_pipe_has_data :: proc(r: ^File) -> (ok: bool, err: Error) {
+ if r == nil || r.impl == nil {
+ return false, nil
+ }
+ fd := __fd(r)
+ poll_fds := []posix.pollfd {
+ posix.pollfd {
+ fd = fd,
+ events = {.IN, .HUP},
+ },
+ }
+ n := posix.poll(raw_data(poll_fds), u32(len(poll_fds)), 0)
+ if n < 0 {
+ return false, _get_platform_error()
+ } else if n != 1 {
+ return false, nil
+ }
+ pipe_events := poll_fds[0].revents
+ if pipe_events >= {.IN} {
+ return true, nil
+ }
+ if pipe_events >= {.HUP} {
+ return false, .Broken_Pipe
+ }
+ return false, nil
+}
diff --git a/core/os/os2/pipe_windows.odin b/core/os/os2/pipe_windows.odin
index ee93fb683..d6dc47c9c 100644
--- a/core/os/os2/pipe_windows.odin
+++ b/core/os/os2/pipe_windows.odin
@@ -15,3 +15,15 @@ _pipe :: proc() -> (r, w: ^File, err: Error) {
return new_file(uintptr(p[0]), ""), new_file(uintptr(p[1]), ""), nil
}
+@(require_results)
+_pipe_has_data :: proc(r: ^File) -> (ok: bool, err: Error) {
+ if r == nil || r.impl == nil {
+ return false, nil
+ }
+ handle := win32.HANDLE((^File_Impl)(r.impl).fd)
+ bytes_available: u32
+ if !win32.PeekNamedPipe(handle, nil, 0, nil, &bytes_available, nil) {
+ return false, _get_platform_error()
+ }
+ return bytes_available > 0, nil
+} \ No newline at end of file
diff --git a/core/os/os2/process.odin b/core/os/os2/process.odin
index ce65987b0..5b5a6e844 100644
--- a/core/os/os2/process.odin
+++ b/core/os/os2/process.odin
@@ -1,16 +1,17 @@
package os2
import "base:runtime"
+
import "core:time"
/*
- In procedures that explicitly state this as one of the allowed values,
- specifies an infinite timeout.
+In procedures that explicitly state this as one of the allowed values,
+specifies an infinite timeout.
*/
TIMEOUT_INFINITE :: time.MIN_DURATION // Note(flysand): Any negative duration will be treated as infinity
/*
- Arguments to the current process.
+Arguments to the current process.
*/
args := get_args()
@@ -24,17 +25,17 @@ get_args :: proc() -> []string {
}
/*
- Exit the current process.
+Exit the current process.
*/
exit :: proc "contextless" (code: int) -> ! {
_exit(code)
}
/*
- Obtain the UID of the current process.
+Obtain the UID of the current process.
- **Note(windows)**: Windows doesn't follow the posix permissions model, so
- the function simply returns -1.
+**Note(windows)**: Windows doesn't follow the posix permissions model, so
+the function simply returns -1.
*/
@(require_results)
get_uid :: proc() -> int {
@@ -42,15 +43,15 @@ get_uid :: proc() -> int {
}
/*
- Obtain the effective UID of the current process.
-
- The effective UID is typically the same as the UID of the process. In case
- the process was run by a user with elevated permissions, the process may
- lower the privilege to perform some tasks without privilege. In these cases
- the real UID of the process and the effective UID are different.
-
- **Note(windows)**: Windows doesn't follow the posix permissions model, so
- the function simply returns -1.
+Obtain the effective UID of the current process.
+
+The effective UID is typically the same as the UID of the process. In case
+the process was run by a user with elevated permissions, the process may
+lower the privilege to perform some tasks without privilege. In these cases
+the real UID of the process and the effective UID are different.
+
+**Note(windows)**: Windows doesn't follow the posix permissions model, so
+the function simply returns -1.
*/
@(require_results)
get_euid :: proc() -> int {
@@ -58,10 +59,10 @@ get_euid :: proc() -> int {
}
/*
- Obtain the GID of the current process.
-
- **Note(windows)**: Windows doesn't follow the posix permissions model, so
- the function simply returns -1.
+Obtain the GID of the current process.
+
+**Note(windows)**: Windows doesn't follow the posix permissions model, so
+the function simply returns -1.
*/
@(require_results)
get_gid :: proc() -> int {
@@ -69,15 +70,15 @@ get_gid :: proc() -> int {
}
/*
- Obtain the effective GID of the current process.
-
- The effective GID is typically the same as the GID of the process. In case
- the process was run by a user with elevated permissions, the process may
- lower the privilege to perform some tasks without privilege. In these cases
- the real GID of the process and the effective GID are different.
-
- **Note(windows)**: Windows doesn't follow the posix permissions model, so
- the function simply returns -1.
+Obtain the effective GID of the current process.
+
+The effective GID is typically the same as the GID of the process. In case
+the process was run by a user with elevated permissions, the process may
+lower the privilege to perform some tasks without privilege. In these cases
+the real GID of the process and the effective GID are different.
+
+**Note(windows)**: Windows doesn't follow the posix permissions model, so
+the function simply returns -1.
*/
@(require_results)
get_egid :: proc() -> int {
@@ -85,7 +86,7 @@ get_egid :: proc() -> int {
}
/*
- Obtain the ID of the current process.
+Obtain the ID of the current process.
*/
@(require_results)
get_pid :: proc() -> int {
@@ -93,13 +94,13 @@ get_pid :: proc() -> int {
}
/*
- Obtain the ID of the parent process.
+Obtain the ID of the parent process.
- **Note(windows)**: Windows does not mantain strong relationships between
- parent and child processes. This function returns the ID of the process
- that has created the current process. In case the parent has died, the ID
- returned by this function can identify a non-existent or a different
- process.
+**Note(windows)**: Windows does not mantain strong relationships between
+parent and child processes. This function returns the ID of the process
+that has created the current process. In case the parent has died, the ID
+returned by this function can identify a non-existent or a different
+process.
*/
@(require_results)
get_ppid :: proc() -> int {
@@ -107,7 +108,7 @@ get_ppid :: proc() -> int {
}
/*
- Obtain ID's of all processes running in the system.
+Obtain ID's of all processes running in the system.
*/
@(require_results)
process_list :: proc(allocator: runtime.Allocator) -> ([]int, Error) {
@@ -115,9 +116,9 @@ process_list :: proc(allocator: runtime.Allocator) -> ([]int, Error) {
}
/*
- Bit set specifying which fields of the `Process_Info` struct need to be
- obtained by the `process_info()` procedure. Each bit corresponds to a
- field in the `Process_Info` struct.
+Bit set specifying which fields of the `Process_Info` struct need to be
+obtained by the `process_info()` procedure. Each bit corresponds to a
+field in the `Process_Info` struct.
*/
Process_Info_Fields :: bit_set[Process_Info_Field]
Process_Info_Field :: enum {
@@ -134,8 +135,8 @@ Process_Info_Field :: enum {
ALL_INFO :: Process_Info_Fields{.Executable_Path, .PPid, .Priority, .Command_Line, .Command_Args, .Environment, .Username, .Working_Dir}
/*
- Contains information about the process as obtained by the `process_info()`
- procedure.
+Contains information about the process as obtained by the `process_info()`
+procedure.
*/
Process_Info :: struct {
// The information about a process the struct contains. `pid` is always
@@ -162,19 +163,19 @@ Process_Info :: struct {
}
/*
- Obtain information about a process.
+Obtain information about a process.
- This procedure obtains an information, specified by `selection` parameter of
- a process given by `pid`.
+This procedure obtains an information, specified by `selection` parameter of
+a process given by `pid`.
- Use `free_process_info` to free the memory allocated by this procedure. The
- `free_process_info` procedure needs to be called, even if this procedure
- returned an error, as some of the fields may have been allocated.
+Use `free_process_info` to free the memory allocated by this procedure. The
+`free_process_info` procedure needs to be called, even if this procedure
+returned an error, as some of the fields may have been allocated.
- **Note**: The resulting information may or may contain the fields specified
- by the `selection` parameter. Always check whether the returned
- `Process_Info` struct has the required fields before checking the error code
- returned by this procedure.
+**Note**: The resulting information may or may contain the fields specified
+by the `selection` parameter. Always check whether the returned
+`Process_Info` struct has the required fields before checking the error code
+returned by this procedure.
*/
@(require_results)
process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) {
@@ -182,20 +183,20 @@ process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator:
}
/*
- Obtain information about a process.
+Obtain information about a process.
- This procedure obtains information, specified by `selection` parameter
- about a process that has been opened by the application, specified in
- the `process` parameter.
+This procedure obtains information, specified by `selection` parameter
+about a process that has been opened by the application, specified in
+the `process` parameter.
- Use `free_process_info` to free the memory allocated by this procedure. The
- `free_process_info` procedure needs to be called, even if this procedure
- returned an error, as some of the fields may have been allocated.
+Use `free_process_info` to free the memory allocated by this procedure. The
+`free_process_info` procedure needs to be called, even if this procedure
+returned an error, as some of the fields may have been allocated.
- **Note**: The resulting information may or may contain the fields specified
- by the `selection` parameter. Always check whether the returned
- `Process_Info` struct has the required fields before checking the error code
- returned by this procedure.
+**Note**: The resulting information may or may contain the fields specified
+by the `selection` parameter. Always check whether the returned
+`Process_Info` struct has the required fields before checking the error code
+returned by this procedure.
*/
@(require_results)
process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) {
@@ -203,19 +204,19 @@ process_info_by_handle :: proc(process: Process, selection: Process_Info_Fields,
}
/*
- Obtain information about the current process.
+Obtain information about the current process.
- This procedure obtains the information, specified by `selection` parameter
- about the currently running process.
+This procedure obtains the information, specified by `selection` parameter
+about the currently running process.
- Use `free_process_info` to free the memory allocated by this procedure. The
- `free_process_info` procedure needs to be called, even if this procedure
- returned an error, as some of the fields may have been allocated.
+Use `free_process_info` to free the memory allocated by this procedure. The
+`free_process_info` procedure needs to be called, even if this procedure
+returned an error, as some of the fields may have been allocated.
- **Note**: The resulting information may or may contain the fields specified
- by the `selection` parameter. Always check whether the returned
- `Process_Info` struct has the required fields before checking the error code
- returned by this procedure.
+**Note**: The resulting information may or may contain the fields specified
+by the `selection` parameter. Always check whether the returned
+`Process_Info` struct has the required fields before checking the error code
+returned by this procedure.
*/
@(require_results)
current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime.Allocator) -> (Process_Info, Error) {
@@ -223,7 +224,7 @@ current_process_info :: proc(selection: Process_Info_Fields, allocator: runtime.
}
/*
- Obtain information about the specified process.
+Obtain information about the specified process.
*/
process_info :: proc {
process_info_by_pid,
@@ -232,11 +233,11 @@ process_info :: proc {
}
/*
- Free the information about the process.
+Free the information about the process.
- This procedure frees the memory occupied by process info using the provided
- allocator. The allocator needs to be the same allocator that was supplied
- to the `process_info` function.
+This procedure frees the memory occupied by process info using the provided
+allocator. The allocator needs to be the same allocator that was supplied
+to the `process_info` function.
*/
free_process_info :: proc(pi: Process_Info, allocator: runtime.Allocator) {
delete(pi.executable_path, allocator)
@@ -254,13 +255,13 @@ free_process_info :: proc(pi: Process_Info, allocator: runtime.Allocator) {
}
/*
- Represents a process handle.
+Represents a process handle.
- When a process dies, the OS is free to re-use the pid of that process. The
- `Process` struct represents a handle to the process that will refer to a
- specific process, even after it has died.
+When a process dies, the OS is free to re-use the pid of that process. The
+`Process` struct represents a handle to the process that will refer to a
+specific process, even after it has died.
- **Note(linux)**: The `handle` will be referring to pidfd.
+**Note(linux)**: The `handle` will be referring to pidfd.
*/
Process :: struct {
pid: int,
@@ -276,13 +277,13 @@ Process_Open_Flag :: enum {
}
/*
- Open a process handle using it's pid.
+Open a process handle using it's pid.
- This procedure obtains a process handle of a process specified by `pid`.
- This procedure can be subject to race conditions. See the description of
- `Process`.
+This procedure obtains a process handle of a process specified by `pid`.
+This procedure can be subject to race conditions. See the description of
+`Process`.
- Use `process_close()` function to close the process handle.
+Use `process_close()` function to close the process handle.
*/
@(require_results)
process_open :: proc(pid: int, flags := Process_Open_Flags {}) -> (Process, Error) {
@@ -322,32 +323,140 @@ Process_Desc :: struct {
}
/*
- Create a new process and obtain its handle.
-
- This procedure creates a new process, with a given command and environment
- strings as parameters. Use `environ()` to inherit the environment of the
- current process.
-
- The `desc` parameter specifies the description of how the process should
- be created. It contains information such as the command line, the
- environment of the process, the starting directory and many other options.
- Most of the fields in the struct can be set to `nil` or an empty value.
-
- Use `process_close` to close the handle to the process. Note, that this
- is not the same as terminating the process. One can terminate the process
- and not close the handle, in which case the handle would be leaked. In case
- the function returns an error, an invalid handle is returned.
-
- This procedure is not thread-safe. It may alter the inheritance properties
- of file handles in an unpredictable manner. In case multiple threads change
- handle inheritance properties, make sure to serialize all those calls.
+Create a new process and obtain its handle.
+
+This procedure creates a new process, with a given command and environment
+strings as parameters. Use `environ()` to inherit the environment of the
+current process.
+
+The `desc` parameter specifies the description of how the process should
+be created. It contains information such as the command line, the
+environment of the process, the starting directory and many other options.
+Most of the fields in the struct can be set to `nil` or an empty value.
+
+Use `process_close` to close the handle to the process. Note, that this
+is not the same as terminating the process. One can terminate the process
+and not close the handle, in which case the handle would be leaked. In case
+the function returns an error, an invalid handle is returned.
+
+This procedure is not thread-safe. It may alter the inheritance properties
+of file handles in an unpredictable manner. In case multiple threads change
+handle inheritance properties, make sure to serialize all those calls.
*/
@(require_results)
-process_start :: proc(desc := Process_Desc {}) -> (Process, Error) {
+process_start :: proc(desc: Process_Desc) -> (Process, Error) {
return _process_start(desc)
}
/*
+Execute the process and capture stdout and stderr streams.
+
+This procedure creates a new process, with a given command and environment
+strings as parameters, and waits until the process finishes execution. While
+the process is running, this procedure accumulates the output of its stdout
+and stderr streams and returns byte slices containing the captured data from
+the streams.
+
+This procedure expects that `stdout` and `stderr` fields of the `desc` parameter
+are left at default, i.e. a `nil` value. You can not capture stdout/stderr and
+redirect it to a file at the same time.
+
+This procedure does not free `stdout` and `stderr` slices before an error is
+returned. Make sure to call `delete` on these slices.
+*/
+@(require_results)
+process_exec :: proc(
+ desc: Process_Desc,
+ allocator: runtime.Allocator,
+ loc := #caller_location,
+) -> (
+ state: Process_State,
+ stdout: []byte,
+ stderr: []byte,
+ err: Error,
+) {
+ assert(desc.stdout == nil, "Cannot redirect stdout when it's being captured", loc)
+ assert(desc.stderr == nil, "Cannot redirect stderr when it's being captured", loc)
+
+ stdout_r, stdout_w := pipe() or_return
+ defer close(stdout_r)
+ stderr_r, stderr_w := pipe() or_return
+ defer close(stderr_r)
+
+ process: Process
+ {
+ // NOTE(flysand): Make sure the write-ends are closed, regardless
+ // of the outcome. This makes read-ends readable on our side.
+ defer close(stdout_w)
+ defer close(stderr_w)
+ desc := desc
+ desc.stdout = stdout_w
+ desc.stderr = stderr_w
+ process = process_start(desc) or_return
+ }
+
+ {
+ stdout_b: [dynamic]byte
+ stdout_b.allocator = allocator
+ defer stdout = stdout_b[:]
+
+ stderr_b: [dynamic]byte
+ stderr_b.allocator = allocator
+ defer stderr = stderr_b[:]
+
+ buf: [1024]u8 = ---
+
+ stdout_done, stderr_done, has_data: bool
+ for err == nil && (!stdout_done || !stderr_done) {
+ n := 0
+
+ if !stdout_done {
+ has_data, err = pipe_has_data(stdout_r)
+ if has_data {
+ n, err = read(stdout_r, buf[:])
+ }
+
+ switch err {
+ case nil:
+ _, err = append(&stdout_b, ..buf[:n])
+ case .EOF, .Broken_Pipe:
+ stdout_done = true
+ err = nil
+ }
+ }
+
+ if err == nil && !stderr_done {
+ n = 0
+ has_data, err = pipe_has_data(stderr_r)
+ if has_data {
+ n, err = read(stderr_r, buf[:])
+ }
+
+ switch err {
+ case nil:
+ _, err = append(&stderr_b, ..buf[:n])
+ case .EOF, .Broken_Pipe:
+ stderr_done = true
+ err = nil
+ }
+ }
+ }
+ }
+
+ if err != nil {
+ state, _ = process_wait(process, timeout=0)
+ if !state.exited {
+ _ = process_kill(process)
+ state, _ = process_wait(process)
+ }
+ return
+ }
+
+ state, err = process_wait(process)
+ return
+}
+
+/*
The state of the process after it has finished execution.
*/
Process_State :: struct {
@@ -371,17 +480,17 @@ Process_State :: struct {
}
/*
- Wait for a process event.
+Wait for a process event.
- This procedure blocks the execution until the process has exited or the
- timeout (if specified) has reached zero. If the timeout is `TIMEOUT_INFINITE`,
- no timeout restriction is imposed and the procedure can block indefinately.
+This procedure blocks the execution until the process has exited or the
+timeout (if specified) has reached zero. If the timeout is `TIMEOUT_INFINITE`,
+no timeout restriction is imposed and the procedure can block indefinately.
- If the timeout has expired, the `General_Error.Timeout` is returned as
- the error.
+If the timeout has expired, the `General_Error.Timeout` is returned as
+the error.
- If an error is returned for any other reason, other than timeout, the
- process state is considered undetermined.
+If an error is returned for any other reason, other than timeout, the
+process state is considered undetermined.
*/
@(require_results)
process_wait :: proc(process: Process, timeout := TIMEOUT_INFINITE) -> (Process_State, Error) {
@@ -389,12 +498,12 @@ process_wait :: proc(process: Process, timeout := TIMEOUT_INFINITE) -> (Process_
}
/*
- Close the handle to a process.
+Close the handle to a process.
- This procedure closes the handle associated with a process. It **does not**
- terminate a process, in case it was running. In case a termination is
- desired, kill the process first, wait for the process to finish,
- then close the handle.
+This procedure closes the handle associated with a process. It **does not**
+terminate a process, in case it was running. In case a termination is
+desired, kill the process first, wait for the process to finish,
+then close the handle.
*/
@(require_results)
process_close :: proc(process: Process) -> (Error) {
@@ -402,10 +511,9 @@ process_close :: proc(process: Process) -> (Error) {
}
/*
- Terminate a process.
-
- This procedure terminates a process, specified by it's handle, `process`.
+Terminate a process.
+This procedure terminates a process, specified by it's handle, `process`.
*/
@(require_results)
process_kill :: proc(process: Process) -> (Error) {
diff --git a/core/os/os2/process_linux.odin b/core/os/os2/process_linux.odin
index ea5ee41b1..7eb4dfa44 100644
--- a/core/os/os2/process_linux.odin
+++ b/core/os/os2/process_linux.odin
@@ -523,7 +523,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
write_errno_to_parent_and_abort :: proc(parent_fd: linux.Fd, errno: linux.Errno) -> ! {
error_byte: [1]u8 = { u8(errno) }
linux.write(parent_fd, error_byte[:])
- intrinsics.trap()
+ linux.exit(126)
}
stdin_fd: linux.Fd
diff --git a/core/os/os2/process_posix.odin b/core/os/os2/process_posix.odin
index 5ac6babc1..b54374cec 100644
--- a/core/os/os2/process_posix.odin
+++ b/core/os/os2/process_posix.odin
@@ -163,7 +163,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
#assert(len(posix.Errno) < max(u8))
errno := u8(posix.errno())
posix.write(parent_fd, &errno, 1)
- runtime.trap()
+ posix.exit(126)
}
null := posix.open("/dev/null", {.RDWR})
@@ -223,7 +223,6 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
return
}
- process.pid = int(pid)
process, _ = _process_open(int(pid), {})
return
}
diff --git a/core/os/os2/process_posix_other.odin b/core/os/os2/process_posix_other.odin
index 77dbfa7eb..65da3e9e2 100644
--- a/core/os/os2/process_posix_other.odin
+++ b/core/os/os2/process_posix_other.odin
@@ -15,6 +15,7 @@ _process_list :: proc(allocator: runtime.Allocator) -> (list: []int, err: Error)
}
_process_open :: proc(pid: int, flags: Process_Open_Flags) -> (process: Process, err: Error) {
+ process.pid = pid
err = .Unsupported
return
}
diff --git a/core/os/os2/process_windows.odin b/core/os/os2/process_windows.odin
index 0c32373f3..1984cbfdf 100644
--- a/core/os/os2/process_windows.odin
+++ b/core/os/os2/process_windows.odin
@@ -442,7 +442,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
stderr_handle = win32.HANDLE((^File_Impl)(desc.stderr.impl).fd)
}
if desc.stdin != nil {
- stdin_handle = win32.HANDLE((^File_Impl)(desc.stderr.impl).fd)
+ stdin_handle = win32.HANDLE((^File_Impl)(desc.stdin.impl).fd)
}
working_dir_w := (win32_utf8_to_wstring(desc.working_dir, temp_allocator()) or_else nil) if len(desc.working_dir) > 0 else nil
@@ -650,26 +650,30 @@ _build_command_line :: proc(command: []string, allocator: runtime.Allocator) ->
strings.write_byte(&builder, ' ')
}
j := 0
- strings.write_byte(&builder, '"')
- for j < len(arg) {
- backslashes := 0
- for j < len(arg) && arg[j] == '\\' {
- backslashes += 1
+ if strings.contains_any(arg, "()[]{}^=;!'+,`~\" ") {
+ strings.write_byte(&builder, '"')
+ for j < len(arg) {
+ backslashes := 0
+ for j < len(arg) && arg[j] == '\\' {
+ backslashes += 1
+ j += 1
+ }
+ if j == len(arg) {
+ _write_byte_n_times(&builder, '\\', 2*backslashes)
+ break
+ } else if arg[j] == '"' {
+ _write_byte_n_times(&builder, '\\', 2*backslashes+1)
+ strings.write_byte(&builder, arg[j])
+ } else {
+ _write_byte_n_times(&builder, '\\', backslashes)
+ strings.write_byte(&builder, arg[j])
+ }
j += 1
}
- if j == len(arg) {
- _write_byte_n_times(&builder, '\\', 2*backslashes)
- break
- } else if arg[j] == '"' {
- _write_byte_n_times(&builder, '\\', 2*backslashes+1)
- strings.write_byte(&builder, '"')
- } else {
- _write_byte_n_times(&builder, '\\', backslashes)
- strings.write_byte(&builder, arg[j])
- }
- j += 1
+ strings.write_byte(&builder, '"')
+ } else {
+ strings.write_string(&builder, arg)
}
- strings.write_byte(&builder, '"')
}
return strings.to_string(builder)
}
diff --git a/core/os/os2/stat_windows.odin b/core/os/os2/stat_windows.odin
index 8ed2a6fed..0a019e9da 100644
--- a/core/os/os2/stat_windows.odin
+++ b/core/os/os2/stat_windows.odin
@@ -200,22 +200,21 @@ _file_type_mode_from_file_attributes :: proc(file_attributes: win32.DWORD, h: wi
} else {
mode |= 0o666
}
+
is_sym := false
if file_attributes & win32.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
is_sym = false
} else {
is_sym = ReparseTag == win32.IO_REPARSE_TAG_SYMLINK || ReparseTag == win32.IO_REPARSE_TAG_MOUNT_POINT
}
+
if is_sym {
type = .Symlink
- } else {
- if file_attributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
- type = .Directory
- mode |= 0o111
- }
- if h != nil {
- type = file_type(h)
- }
+ } else if file_attributes & win32.FILE_ATTRIBUTE_DIRECTORY != 0 {
+ type = .Directory
+ mode |= 0o111
+ } else if h != nil {
+ type = file_type(h)
}
return
}
diff --git a/core/os/os_darwin.odin b/core/os/os_darwin.odin
index 371485a47..b09a41fbf 100644
--- a/core/os/os_darwin.odin
+++ b/core/os/os_darwin.odin
@@ -1061,7 +1061,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (path: string, err: Error) {
}
@(require_results)
-absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
+absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) {
rel := rel
if rel == "" {
rel = "."
@@ -1076,9 +1076,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
}
defer _unix_free(rawptr(path_ptr))
- path = strings.clone(string(path_ptr))
-
- return path, nil
+ return strings.clone(string(path_ptr), allocator)
}
access :: proc(path: string, mask: int) -> bool {
diff --git a/core/os/os_freebsd.odin b/core/os/os_freebsd.odin
index f617cf973..837e79f4d 100644
--- a/core/os/os_freebsd.odin
+++ b/core/os/os_freebsd.odin
@@ -789,7 +789,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {
}
@(require_results)
-absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
+absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) {
rel := rel
if rel == "" {
rel = "."
@@ -804,10 +804,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
}
defer _unix_free(rawptr(path_ptr))
-
- path = strings.clone(string(path_ptr))
-
- return path, nil
+ return strings.clone(string(path_ptr), allocator)
}
access :: proc(path: string, mask: int) -> (bool, Error) {
diff --git a/core/os/os_haiku.odin b/core/os/os_haiku.odin
index 0d2c334be..4ad370724 100644
--- a/core/os/os_haiku.odin
+++ b/core/os/os_haiku.odin
@@ -431,7 +431,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {
}
@(require_results)
-absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
+absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) {
rel := rel
if rel == "" {
rel = "."
@@ -447,9 +447,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
defer _unix_free(path_ptr)
path_cstr := cstring(path_ptr)
- path = strings.clone(string(path_cstr))
-
- return path, nil
+ return strings.clone(string(path_cstr), allocator)
}
access :: proc(path: string, mask: int) -> (bool, Error) {
diff --git a/core/os/os_linux.odin b/core/os/os_linux.odin
index 8c8cd7f73..e023ce7cb 100644
--- a/core/os/os_linux.odin
+++ b/core/os/os_linux.odin
@@ -242,10 +242,13 @@ F_SETFL: int : 4 /* Set file flags */
// NOTE(zangent): These are OS specific!
// Do not mix these up!
-RTLD_LAZY :: 0x001
-RTLD_NOW :: 0x002
-RTLD_BINDING_MASK :: 0x3
-RTLD_GLOBAL :: 0x100
+RTLD_LAZY :: 0x0001
+RTLD_NOW :: 0x0002
+RTLD_BINDING_MASK :: 0x0003
+RTLD_GLOBAL :: 0x0100
+RTLD_NOLOAD :: 0x0004
+RTLD_DEEPBIND :: 0x0008
+RTLD_NODELETE :: 0x1000
socklen_t :: c.int
@@ -487,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 ---
@@ -914,7 +917,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {
}
@(require_results)
-absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
+absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) {
rel := rel
if rel == "" {
rel = "."
@@ -929,9 +932,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
}
defer _unix_free(rawptr(path_ptr))
- path = strings.clone(string(path_ptr))
-
- return path, nil
+ return strings.clone(string(path_ptr), allocator)
}
access :: proc(path: string, mask: int) -> (bool, Error) {
diff --git a/core/os/os_netbsd.odin b/core/os/os_netbsd.odin
index 493527803..e3ba760a4 100644
--- a/core/os/os_netbsd.odin
+++ b/core/os/os_netbsd.odin
@@ -844,7 +844,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (path: string, err: Error) {
}
@(require_results)
-absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
+absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) {
rel := rel
if rel == "" {
rel = "."
@@ -859,9 +859,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
}
defer _unix_free(rawptr(path_ptr))
- path = strings.clone(string(path_ptr))
-
- return path, nil
+ return strings.clone(string(path_ptr), allocator)
}
access :: proc(path: string, mask: int) -> (bool, Error) {
diff --git a/core/os/os_openbsd.odin b/core/os/os_openbsd.odin
index 62872d9dc..3c377968c 100644
--- a/core/os/os_openbsd.odin
+++ b/core/os/os_openbsd.odin
@@ -758,7 +758,7 @@ absolute_path_from_handle :: proc(fd: Handle) -> (string, Error) {
}
@(require_results)
-absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
+absolute_path_from_relative :: proc(rel: string, allocator := context.allocator) -> (path: string, err: Error) {
rel := rel
if rel == "" {
rel = "."
@@ -773,9 +773,7 @@ absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Error) {
}
defer _unix_free(rawptr(path_ptr))
- path = strings.clone(string(path_ptr))
-
- return path, nil
+ return strings.clone(string(path_ptr), allocator)
}
access :: proc(path: string, mask: int) -> (bool, Error) {
diff --git a/core/path/filepath/match.odin b/core/path/filepath/match.odin
index 7eb72b9a7..003f8046d 100644
--- a/core/path/filepath/match.odin
+++ b/core/path/filepath/match.odin
@@ -246,6 +246,13 @@ glob :: proc(pattern: string, allocator := context.allocator) -> (matches: []str
if err != .None {
return
}
+ defer {
+ for s in m {
+ delete(s)
+ }
+ delete(m)
+ }
+
dmatches := make([dynamic]string, 0, 0)
for d in m {
dmatches, err = _glob(d, file, &dmatches)
diff --git a/core/path/filepath/path_unix.odin b/core/path/filepath/path_unix.odin
index a18dc739e..35b98a7ae 100644
--- a/core/path/filepath/path_unix.odin
+++ b/core/path/filepath/path_unix.odin
@@ -1,14 +1,10 @@
#+build linux, darwin, freebsd, openbsd, netbsd
package filepath
-when ODIN_OS == .Darwin {
- foreign import libc "system:System.framework"
-} else {
- foreign import libc "system:c"
-}
-
import "base:runtime"
+
import "core:strings"
+import "core:sys/posix"
SEPARATOR :: '/'
SEPARATOR_STRING :: `/`
@@ -28,11 +24,11 @@ abs :: proc(path: string, allocator := context.allocator) -> (string, bool) {
rel = "."
}
rel_cstr := strings.clone_to_cstring(rel, context.temp_allocator)
- path_ptr := realpath(rel_cstr, nil)
+ path_ptr := posix.realpath(rel_cstr, nil)
if path_ptr == nil {
- return "", __error()^ == 0
+ return "", posix.errno() == nil
}
- defer _unix_free(rawptr(path_ptr))
+ defer posix.free(path_ptr)
path_str := strings.clone(string(path_ptr), allocator)
return path_str, true
@@ -48,26 +44,3 @@ join :: proc(elems: []string, allocator := context.allocator) -> (joined: string
}
return "", nil
}
-
-@(private)
-foreign libc {
- realpath :: proc(path: cstring, resolved_path: [^]byte = nil) -> cstring ---
- @(link_name="free") _unix_free :: proc(ptr: rawptr) ---
-
-}
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD {
- @(private)
- foreign libc {
- @(link_name="__error") __error :: proc() -> ^i32 ---
- }
-} else when ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD {
- @(private)
- foreign libc {
- @(link_name="__errno") __error :: proc() -> ^i32 ---
- }
-} else {
- @(private)
- foreign libc {
- @(link_name="__errno_location") __error :: proc() -> ^i32 ---
- }
-}
diff --git a/core/reflect/reflect.odin b/core/reflect/reflect.odin
index c04afb380..7f79acb77 100644
--- a/core/reflect/reflect.odin
+++ b/core/reflect/reflect.odin
@@ -31,8 +31,6 @@ Type_Info_Enum :: runtime.Type_Info_Enum
Type_Info_Map :: runtime.Type_Info_Map
Type_Info_Bit_Set :: runtime.Type_Info_Bit_Set
Type_Info_Simd_Vector :: runtime.Type_Info_Simd_Vector
-Type_Info_Relative_Pointer :: runtime.Type_Info_Relative_Pointer
-Type_Info_Relative_Multi_Pointer :: runtime.Type_Info_Relative_Multi_Pointer
Type_Info_Matrix :: runtime.Type_Info_Matrix
Type_Info_Soa_Pointer :: runtime.Type_Info_Soa_Pointer
Type_Info_Bit_Field :: runtime.Type_Info_Bit_Field
@@ -67,8 +65,6 @@ Type_Kind :: enum {
Map,
Bit_Set,
Simd_Vector,
- Relative_Pointer,
- Relative_Multi_Pointer,
Matrix,
Soa_Pointer,
Bit_Field,
@@ -80,35 +76,33 @@ type_kind :: proc(T: typeid) -> Type_Kind {
ti := type_info_of(T)
if ti != nil {
switch _ in ti.variant {
- case Type_Info_Named: return .Named
- case Type_Info_Integer: return .Integer
- case Type_Info_Rune: return .Rune
- case Type_Info_Float: return .Float
- case Type_Info_Complex: return .Complex
- case Type_Info_Quaternion: return .Quaternion
- case Type_Info_String: return .String
- case Type_Info_Boolean: return .Boolean
- case Type_Info_Any: return .Any
- case Type_Info_Type_Id: return .Type_Id
- case Type_Info_Pointer: return .Pointer
- case Type_Info_Multi_Pointer: return .Multi_Pointer
- case Type_Info_Procedure: return .Procedure
- case Type_Info_Array: return .Array
- case Type_Info_Enumerated_Array: return .Enumerated_Array
- case Type_Info_Dynamic_Array: return .Dynamic_Array
- case Type_Info_Slice: return .Slice
- case Type_Info_Parameters: return .Tuple
- case Type_Info_Struct: return .Struct
- case Type_Info_Union: return .Union
- case Type_Info_Enum: return .Enum
- case Type_Info_Map: return .Map
- case Type_Info_Bit_Set: return .Bit_Set
- case Type_Info_Simd_Vector: return .Simd_Vector
- case Type_Info_Relative_Pointer: return .Relative_Pointer
- case Type_Info_Relative_Multi_Pointer: return .Relative_Multi_Pointer
- case Type_Info_Matrix: return .Matrix
- case Type_Info_Soa_Pointer: return .Soa_Pointer
- case Type_Info_Bit_Field: return .Bit_Field
+ case Type_Info_Named: return .Named
+ case Type_Info_Integer: return .Integer
+ case Type_Info_Rune: return .Rune
+ case Type_Info_Float: return .Float
+ case Type_Info_Complex: return .Complex
+ case Type_Info_Quaternion: return .Quaternion
+ case Type_Info_String: return .String
+ case Type_Info_Boolean: return .Boolean
+ case Type_Info_Any: return .Any
+ case Type_Info_Type_Id: return .Type_Id
+ case Type_Info_Pointer: return .Pointer
+ case Type_Info_Multi_Pointer: return .Multi_Pointer
+ case Type_Info_Procedure: return .Procedure
+ case Type_Info_Array: return .Array
+ case Type_Info_Enumerated_Array: return .Enumerated_Array
+ case Type_Info_Dynamic_Array: return .Dynamic_Array
+ case Type_Info_Slice: return .Slice
+ case Type_Info_Parameters: return .Tuple
+ case Type_Info_Struct: return .Struct
+ case Type_Info_Union: return .Union
+ case Type_Info_Enum: return .Enum
+ case Type_Info_Map: return .Map
+ case Type_Info_Bit_Set: return .Bit_Set
+ case Type_Info_Simd_Vector: return .Simd_Vector
+ case Type_Info_Matrix: return .Matrix
+ case Type_Info_Soa_Pointer: return .Soa_Pointer
+ case Type_Info_Bit_Field: return .Bit_Field
}
}
@@ -723,6 +717,27 @@ enum_name_from_value_any :: proc(value: any) -> (name: string, ok: bool) {
return
}
+/*
+Returns whether the value given has a defined name in the enum type.
+*/
+@(require_results)
+enum_value_has_name :: proc(value: $T) -> bool where intrinsics.type_is_enum(T) {
+ when len(T) == cap(T) {
+ return value >= min(T) && value <= max(T)
+ } else {
+ if value < min(T) || value > max(T) {
+ return false
+ }
+
+ for valid_value in T {
+ if valid_value == value {
+ return true
+ }
+ }
+
+ return false
+ }
+}
@@ -1468,21 +1483,6 @@ as_string :: proc(a: any) -> (value: string, valid: bool) {
}
@(require_results)
-relative_pointer_to_absolute :: proc(a: any) -> rawptr {
- if a == nil { return nil }
- a := a
- ti := runtime.type_info_core(type_info_of(a.id))
- a.id = ti.id
-
- #partial switch info in ti.variant {
- case Type_Info_Relative_Pointer:
- return relative_pointer_to_absolute_raw(a.data, info.base_integer.id)
- }
- return nil
-}
-
-
-@(require_results)
relative_pointer_to_absolute_raw :: proc(data: rawptr, base_integer_id: typeid) -> rawptr {
_handle :: proc(ptr: ^$T) -> rawptr where intrinsics.type_is_integer(T) {
if ptr^ == 0 {
@@ -1543,10 +1543,6 @@ as_pointer :: proc(a: any) -> (value: rawptr, valid: bool) {
case cstring: value = rawptr(v)
case: valid = false
}
-
- case Type_Info_Relative_Pointer:
- valid = true
- value = relative_pointer_to_absolute_raw(a.data, info.base_integer.id)
}
return
@@ -1656,8 +1652,6 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
Type_Info_Bit_Set,
Type_Info_Enum,
Type_Info_Simd_Vector,
- Type_Info_Relative_Pointer,
- Type_Info_Relative_Multi_Pointer,
Type_Info_Soa_Pointer,
Type_Info_Matrix:
return runtime.memory_compare(a.data, b.data, t.size) == 0
diff --git a/core/reflect/types.odin b/core/reflect/types.odin
index 4f0674dc8..cb31a27e2 100644
--- a/core/reflect/types.odin
+++ b/core/reflect/types.odin
@@ -158,14 +158,6 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool {
case Type_Info_Simd_Vector:
y := b.variant.(Type_Info_Simd_Vector) or_return
return x.count == y.count && x.elem == y.elem
-
- case Type_Info_Relative_Pointer:
- y := b.variant.(Type_Info_Relative_Pointer) or_return
- return x.base_integer == y.base_integer && x.pointer == y.pointer
-
- case Type_Info_Relative_Multi_Pointer:
- y := b.variant.(Type_Info_Relative_Multi_Pointer) or_return
- return x.base_integer == y.base_integer && x.pointer == y.pointer
case Type_Info_Matrix:
y := b.variant.(Type_Info_Matrix) or_return
@@ -392,18 +384,6 @@ is_simd_vector :: proc(info: ^Type_Info) -> bool {
_, ok := type_info_base(info).variant.(Type_Info_Simd_Vector)
return ok
}
-@(require_results)
-is_relative_pointer :: proc(info: ^Type_Info) -> bool {
- if info == nil { return false }
- _, ok := type_info_base(info).variant.(Type_Info_Relative_Pointer)
- return ok
-}
-@(require_results)
-is_relative_multi_pointer :: proc(info: ^Type_Info) -> bool {
- if info == nil { return false }
- _, ok := type_info_base(info).variant.(Type_Info_Relative_Multi_Pointer)
- return ok
-}
@(require_results)
@@ -736,18 +716,6 @@ write_type_writer :: #force_no_inline proc(w: io.Writer, ti: ^Type_Info, n_writt
io.write_i64(w, i64(info.count), 10, &n) or_return
io.write_byte(w, ']', &n) or_return
write_type(w, info.elem, &n) or_return
-
- case Type_Info_Relative_Pointer:
- io.write_string(w, "#relative(", &n) or_return
- write_type(w, info.base_integer, &n) or_return
- io.write_string(w, ") ", &n) or_return
- write_type(w, info.pointer, &n) or_return
-
- case Type_Info_Relative_Multi_Pointer:
- io.write_string(w, "#relative(", &n) or_return
- write_type(w, info.base_integer, &n) or_return
- io.write_string(w, ") ", &n) or_return
- write_type(w, info.pointer, &n) or_return
case Type_Info_Matrix:
if info.layout == .Row_Major {
diff --git a/core/simd/x86/sse2.odin b/core/simd/x86/sse2.odin
index aaddbe6b4..4c231c377 100644
--- a/core/simd/x86/sse2.odin
+++ b/core/simd/x86/sse2.odin
@@ -240,7 +240,7 @@ _mm_sll_epi64 :: #force_inline proc "c" (a, count: __m128i) -> __m128i {
}
@(require_results, enable_target_feature="sse2")
_mm_srai_epi16 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i {
- return transmute(__m128i)psraiw(transmute(i16x8)a. IMM8)
+ return transmute(__m128i)psraiw(transmute(i16x8)a, IMM8)
}
@(require_results, enable_target_feature="sse2")
_mm_sra_epi16 :: #force_inline proc "c" (a, count: __m128i) -> __m128i {
@@ -262,7 +262,7 @@ _mm_srli_si128 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i {
}
@(require_results, enable_target_feature="sse2")
_mm_srli_epi16 :: #force_inline proc "c" (a: __m128i, $IMM8: u32) -> __m128i {
- return transmute(__m128i)psrliw(transmute(i16x8)a. IMM8)
+ return transmute(__m128i)psrliw(transmute(i16x8)a, IMM8)
}
@(require_results, enable_target_feature="sse2")
_mm_srl_epi16 :: #force_inline proc "c" (a, count: __m128i) -> __m128i {
diff --git a/core/slice/map.odin b/core/slice/map.odin
index 545ba8305..c68488d26 100644
--- a/core/slice/map.odin
+++ b/core/slice/map.odin
@@ -48,11 +48,11 @@ map_entries :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries
return
}
-map_entry_infos :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries: []Map_Entry_Info(K, V)) #no_bounds_check {
+map_entry_infos :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (entries: []Map_Entry_Info(K, V), err: runtime.Allocator_Error) #no_bounds_check {
m := m
rm := (^runtime.Raw_Map)(&m)
- info := type_info_base(type_info_of(M)).variant.(Type_Info_Map)
+ info := runtime.type_info_base(type_info_of(M)).variant.(runtime.Type_Info_Map)
if info.map_info != nil {
entries = make(type_of(entries), len(m), allocator) or_return
@@ -61,8 +61,8 @@ map_entry_infos :: proc(m: $M/map[$K]$V, allocator := context.allocator) -> (ent
entry_index := 0
for bucket_index in 0..<map_cap {
if hash := hs[bucket_index]; runtime.map_hash_is_valid(hash) {
- key := runtime.map_cell_index_dynamic(ks, &info.map_info.ks, bucket_index)
- value := runtime.map_cell_index_dynamic(vs, &info.map_info.vs, bucket_index)
+ key := runtime.map_cell_index_dynamic(ks, info.map_info.ks, bucket_index)
+ value := runtime.map_cell_index_dynamic(vs, info.map_info.vs, bucket_index)
entries[entry_index].hash = hash
entries[entry_index].key = (^K)(key)^
entries[entry_index].value = (^V)(value)^
diff --git a/core/slice/slice.odin b/core/slice/slice.odin
index 99ad15547..c31edf281 100644
--- a/core/slice/slice.odin
+++ b/core/slice/slice.odin
@@ -37,6 +37,17 @@ to_bytes :: proc "contextless" (s: []$T) -> []byte {
}
/*
+ Turns a byte slice into a type.
+*/
+@(require_results)
+to_type :: proc(buf: []u8, $T: typeid) -> (T, bool) #optional_ok {
+ if len(buf) < size_of(T) {
+ return {}, false
+ }
+ return intrinsics.unaligned_load((^T)(raw_data(buf))), true
+}
+
+/*
Turn a slice of one type, into a slice of another type.
Only converts the type and length of the slice itself.
@@ -96,9 +107,37 @@ contains :: proc(array: $T/[]$E, value: E) -> bool where intrinsics.type_is_comp
return found
}
+/*
+Searches the given slice for the given element in O(n) time.
+
+If you need a custom search condition, see `linear_search_proc`
+
+Inputs:
+- array: The slice to search in.
+- key: The element to search for.
+
+Returns:
+- index: The index `i`, such that `array[i]` is the first occurrence of `key` in `array`, or -1 if `key` is not present in `array`.
+
+Example:
+ index: int
+ found: bool
+
+ a := []i32{10, 10, 10, 20}
+
+ index, found = linear_search_reverse(a, 10)
+ assert(index == 0 && found == true)
+
+ index, found = linear_search_reverse(a, 30)
+ assert(index == -1 && found == false)
+
+ // Note that `index == 1`, since it is relative to `a[2:]`
+ index, found = linear_search_reverse(a[2:], 20)
+ assert(index == 1 && found == true)
+*/
@(require_results)
linear_search :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool)
- where intrinsics.type_is_comparable(T) #no_bounds_check {
+ where intrinsics.type_is_comparable(T) {
for x, i in array {
if x == key {
return i, true
@@ -107,8 +146,18 @@ linear_search :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool)
return -1, false
}
+/*
+Searches the given slice for the first element satisfying predicate `f` in O(n) time.
+
+Inputs:
+- array: The slice to search in.
+- f: The search condition.
+
+Returns:
+- index: The index `i`, such that `array[i]` is the first `x` in `array` for which `f(x) == true`, or -1 if such `x` does not exist.
+*/
@(require_results)
-linear_search_proc :: proc(array: $A/[]$T, f: proc(T) -> bool) -> (index: int, found: bool) #no_bounds_check {
+linear_search_proc :: proc(array: $A/[]$T, f: proc(T) -> bool) -> (index: int, found: bool) {
for x, i in array {
if f(x) {
return i, true
@@ -118,22 +167,88 @@ linear_search_proc :: proc(array: $A/[]$T, f: proc(T) -> bool) -> (index: int, f
}
/*
- Binary search searches the given slice for the given element.
- If the slice is not sorted, the returned index is unspecified and meaningless.
+Searches the given slice for the given element in O(n) time, starting from the
+slice end.
+
+If you need a custom search condition, see `linear_search_reverse_proc`
+
+Inputs:
+- array: The slice to search in.
+- key: The element to search for.
- If the value is found then the returned int is the index of the matching element.
- If there are multiple matches, then any one of the matches could be returned.
+Returns:
+- index: The index `i`, such that `array[i]` is the last occurrence of `key` in `array`, or -1 if `key` is not present in `array`.
- If the value is not found then the returned int is the index where a matching
- element could be inserted while maintaining sorted order.
+Example:
+ index: int
+ found: bool
+
+ a := []i32{10, 10, 10, 20}
+
+ index, found = linear_search_reverse(a, 20)
+ assert(index == 3 && found == true)
- # Examples
+ index, found = linear_search_reverse(a, 10)
+ assert(index == 2 && found == true)
+ index, found = linear_search_reverse(a, 30)
+ assert(index == -1 && found == false)
+
+ // Note that `index == 1`, since it is relative to `a[2:]`
+ index, found = linear_search_reverse(a[2:], 20)
+ assert(index == 1 && found == true)
+*/
+@(require_results)
+linear_search_reverse :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool)
+ where intrinsics.type_is_comparable(T) {
+ #reverse for x, i in array {
+ if x == key {
+ return i, true
+ }
+ }
+ return -1, false
+}
+
+/*
+Searches the given slice for the last element satisfying predicate `f` in O(n)
+time, starting from the slice end.
+
+Inputs:
+- array: The slice to search in.
+- f: The search condition.
+
+Returns:
+- index: The index `i`, such that `array[i]` is the last `x` in `array` for which `f(x) == true`, or -1 if such `x` does not exist.
+*/
+@(require_results)
+linear_search_reverse_proc :: proc(array: $A/[]$T, f: proc(T) -> bool) -> (index: int, found: bool) {
+ #reverse for x, i in array {
+ if f(x) {
+ return i, true
+ }
+ }
+ return -1, false
+}
+
+/*
+Searches the given slice for the given element.
+If the slice is not sorted, the returned index is unspecified and meaningless.
+
+If the value is found then the returned int is the index of the matching element.
+If there are multiple matches, then any one of the matches could be returned.
+
+If the value is not found then the returned int is the index where a matching
+element could be inserted while maintaining sorted order.
+
+For slices of more complex types see: `binary_search_by`
+
+Example:
+ /*
Looks up a series of four elements. The first is found, with a
uniquely determined position; the second and third are not
found; the fourth could match any position in `[1, 4]`.
+ */
- ```
index: int
found: bool
@@ -150,9 +265,6 @@ linear_search_proc :: proc(array: $A/[]$T, f: proc(T) -> bool) -> (index: int, f
index, found = slice.binary_search(s, 1)
assert(index >= 1 && index <= 4 && found == true)
- ```
-
- For slices of more complex types see: binary_search_by
*/
@(require_results)
binary_search :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool)
@@ -161,21 +273,21 @@ binary_search :: proc(array: $A/[]$T, key: T) -> (index: int, found: bool)
}
/*
- Binary search searches the given slice for the given element.
- If the slice is not sorted, the returned index is unspecified and meaningless.
+Searches the given slice for the given element.
+If the slice is not sorted, the returned index is unspecified and meaningless.
- If the value is found then the returned int is the index of the matching element.
- If there are multiple matches, then any one of the matches could be returned.
+If the value is found then the returned int is the index of the matching element.
+If there are multiple matches, then any one of the matches could be returned.
- If the value is not found then the returned int is the index where a matching
- element could be inserted while maintaining sorted order.
+If the value is not found then the returned int is the index where a matching
+element could be inserted while maintaining sorted order.
- The array elements and key may be different types. This allows the filter procedure
- to compare keys against a slice of structs, one struct value at a time.
+The array elements and key may be different types. This allows the filter procedure
+to compare keys against a slice of structs, one struct value at a time.
- Returns:
- index: int
- found: bool
+Returns:
+- index: int
+- found: bool
*/
@(require_results)
@@ -359,6 +471,12 @@ is_empty :: proc(a: $T/[]$E) -> bool {
return len(a) == 0
}
+// Gets the byte size of the backing data
+@(require_results)
+size :: proc "contextless" (a: $T/[]$E) -> int {
+ return len(a) * size_of(E)
+}
+
@(require_results)
diff --git a/core/strconv/strconv.odin b/core/strconv/strconv.odin
index b1155c22f..26a737bd1 100644
--- a/core/strconv/strconv.odin
+++ b/core/strconv/strconv.odin
@@ -1121,6 +1121,7 @@ parse_f64_prefix :: proc(str: string) -> (value: f64, nr: int, ok: bool) {
break trunc_block
}
f := f64(mantissa)
+ f_abs := f
if neg {
f = -f
}
@@ -1132,7 +1133,7 @@ parse_f64_prefix :: proc(str: string) -> (value: f64, nr: int, ok: bool) {
f *= pow10[exp-22]
exp = 22
}
- if f > 1e15 || f < 1e-15 {
+ if f_abs > 1e15 || f_abs < 1e-15 {
break trunc_block
}
return f * pow10[exp], nr, true
diff --git a/core/strings/strings.odin b/core/strings/strings.odin
index dbc84f8b7..af93ff33c 100644
--- a/core/strings/strings.odin
+++ b/core/strings/strings.odin
@@ -752,7 +752,7 @@ cut :: proc(s: string, rune_offset := int(0), rune_length := int(0)) -> (res: st
count += 1
}
- if rune_length <= 1 {
+ if rune_length < 1 {
return s
}
diff --git a/core/sys/darwin/Foundation/NSApplication.odin b/core/sys/darwin/Foundation/NSApplication.odin
index 7191f6d07..254da75ad 100644
--- a/core/sys/darwin/Foundation/NSApplication.odin
+++ b/core/sys/darwin/Foundation/NSApplication.odin
@@ -108,6 +108,16 @@ Application_setMainMenu :: proc "c" (self: ^Application, menu: ^Menu) {
msgSend(nil, self, "setMainMenu:", menu)
}
+@(objc_type=Application, objc_name="mainWindow")
+Application_mainWindow :: proc "c" (self: ^Application) -> ^Window {
+ return msgSend(^Window, self, "mainWindow")
+}
+
+@(objc_type=Application, objc_name="keyWindow")
+Application_keyWindow :: proc "c" (self: ^Application) -> ^Window {
+ return msgSend(^Window, self, "keyWindow")
+}
+
@(objc_type=Application, objc_name="windows")
Application_windows :: proc "c" (self: ^Application) -> ^Array {
return msgSend(^Array, self, "windows")
diff --git a/core/sys/darwin/mach_darwin.odin b/core/sys/darwin/mach_darwin.odin
index 1e728fe78..01affa6e8 100644
--- a/core/sys/darwin/mach_darwin.odin
+++ b/core/sys/darwin/mach_darwin.odin
@@ -3,17 +3,18 @@ package darwin
foreign import mach "system:System.framework"
import "core:c"
+import "base:intrinsics"
// NOTE(tetra): Unclear whether these should be aligned 16 or not.
// However all other sync primitives are aligned for robustness.
// I cannot currently align these though.
// See core/sys/unix/pthread_linux.odin/pthread_t.
-mach_port_t :: distinct i32
+mach_port_t :: distinct c.uint
task_t :: mach_port_t
semaphore_t :: distinct u64
-kern_return_t :: distinct u64
+kern_return_t :: distinct c.int
thread_act_t :: distinct u64
thread_state_t :: distinct ^u32
thread_list_t :: [^]thread_act_t
@@ -37,11 +38,6 @@ MACH_MSGH_BITS_COMPLEX :: 0x80000000
MACH_PORT_RIGHT_SEND :: 0
MACH_PORT_RIGHT_RECEIVE :: 1
-VM_PROT_NONE :: 0
-VM_PROT_READ :: 1
-VM_PROT_WRITE :: 2
-VM_PROT_EXECUTE :: 4
-
VM_INHERIT_SHARE :: 0
VM_INHERIT_COPY :: 1
VM_INHERIT_NONE :: 2
@@ -58,6 +54,27 @@ ARM_THREAD_STATE64 :: 6
mach_msg_option_t :: distinct i32
name_t :: distinct cstring
+vm_map_t :: mach_port_t
+mem_entry_name_port_t :: mach_port_t
+ipc_space_t :: mach_port_t
+thread_t :: mach_port_t
+
+vm_size_t :: distinct c.uintptr_t
+
+vm_address_t :: vm_offset_t
+vm_offset_t :: distinct c.uintptr_t
+
+// NOTE(beau): typedefed to int in the original headers
+boolean_t :: b32
+
+vm_prot_t :: distinct c.int
+
+vm_inherit_t :: distinct c.uint
+
+mach_port_name_t :: distinct c.uint
+
+sync_policy_t :: distinct c.int
+
mach_msg_port_descriptor_t :: struct {
name: mach_port_t,
_: u32,
@@ -257,20 +274,489 @@ foreign mach {
task_info :: proc(task: task_t, flavor: i32, info: task_info_t, count: ^u32) -> kern_return_t ---
task_terminate :: proc(task: task_t) -> kern_return_t ---
+ semaphore_create :: proc(task: task_t, semaphore: ^semaphore_t, policy: Sync_Policy, value: c.int) -> Kern_Return ---
+ semaphore_destroy :: proc(task: task_t, semaphore: semaphore_t) -> Kern_Return ---
+
+ semaphore_signal :: proc(semaphore: semaphore_t) -> Kern_Return ---
+ semaphore_signal_all :: proc(semaphore: semaphore_t) -> Kern_Return ---
+ semaphore_signal_thread :: proc(semaphore: semaphore_t, thread: thread_t) -> Kern_Return ---
+
+ semaphore_wait :: proc(semaphore: semaphore_t) -> Kern_Return ---
+
thread_get_state :: proc(thread: thread_act_t, flavor: i32, thread_state: thread_state_t, old_state_count: ^u32) -> kern_return_t ---
thread_info :: proc(thread: thread_act_t, flavor: u32, thread_info: ^thread_identifier_info, info_count: ^u32) -> kern_return_t ---
bootstrap_register2 :: proc(bp: mach_port_t, service_name: name_t, sp: mach_port_t, flags: u64) -> kern_return_t ---
bootstrap_look_up :: proc(bp: mach_port_t, service_name: name_t, sp: ^mach_port_t) -> kern_return_t ---
- semaphore_create :: proc(task: task_t, semaphore: ^semaphore_t, policy, value: c.int) -> kern_return_t ---
- semaphore_destroy :: proc(task: task_t, semaphore: semaphore_t) -> kern_return_t ---
+ vm_map :: proc(
+ target_task: vm_map_t,
+ address: ^vm_address_t,
+ size: vm_size_t,
+ mask: vm_address_t,
+ flags: VM_Flags,
+ object: mem_entry_name_port_t,
+ offset: vm_offset_t,
+ copy: boolean_t,
+ cur_protection,
+ max_protection: VM_Prot_Flags,
+ inheritance: VM_Inherit,
+ ) -> Kern_Return ---
+
+ mach_make_memory_entry :: proc(
+ target_task: vm_map_t,
+ size: ^vm_size_t,
+ offset: vm_offset_t,
+ permission: VM_Prot_Flags,
+ object_handle: ^mem_entry_name_port_t,
+ parent_entry: mem_entry_name_port_t,
+ ) -> Kern_Return ---
+}
+
+
+
+Kern_Return :: enum kern_return_t {
+ Success,
+
+ /* Specified address is not currently valid.
+ */
+ Invalid_Address,
+
+ /* Specified memory is valid, but does not permit the
+ * required forms of access.
+ */
+ Protection_Failure,
+
+ /* The address range specified is already in use, or
+ * no address range of the size specified could be
+ * found.
+ */
+ No_Space,
+
+ /* The function requested was not applicable to this
+ * type of argument, or an argument is invalid
+ */
+ Invalid_Argument,
+
+ /* The function could not be performed. A catch-all.
+ */
+ Failure,
+
+ /* A system resource could not be allocated to fulfill
+ * this request. This failure may not be permanent.
+ */
+ Resource_Shortage,
+
+ /* The task in question does not hold receive rights
+ * for the port argument.
+ */
+ Not_Receiver,
+
+ /* Bogus access restriction.
+ */
+ No_Access,
+
+ /* During a page fault, the target address refers to a
+ * memory object that has been destroyed. This
+ * failure is permanent.
+ */
+ Memory_Failure,
+
+ /* During a page fault, the memory object indicated
+ * that the data could not be returned. This failure
+ * may be temporary; future attempts to access this
+ * same data may succeed, as defined by the memory
+ * object.
+ */
+ Memory_Error,
+
+ /* The receive right is already a member of the portset.
+ */
+ Already_In_Set,
+
+ /* The receive right is not a member of a port set.
+ */
+ Not_In_Set,
+
+ /* The name already denotes a right in the task.
+ */
+ Name_Exists,
+
+ /* The operation was aborted. Ipc code will
+ * catch this and reflect it as a message error.
+ */
+ Aborted,
+
+ /* The name doesn't denote a right in the task.
+ */
+ Invalid_Name,
+
+ /* Target task isn't an active task.
+ */
+ Invalid_Task,
+
+ /* The name denotes a right, but not an appropriate right.
+ */
+ Invalid_Right,
+
+ /* A blatant range error.
+ */
+ Invalid_Value,
+
+ /* Operation would overflow limit on user-references.
+ */
+ URefs_Overflow,
+
+ /* The supplied (port) capability is improper.
+ */
+ Invalid_Capability,
+
+ /* The task already has send or receive rights
+ * for the port under another name.
+ */
+ Right_Exists,
+
+ /* Target host isn't actually a host.
+ */
+ Invalid_Host,
+
+ /* An attempt was made to supply "precious" data
+ * for memory that is already present in a
+ * memory object.
+ */
+ Memory_Present,
+
+ /* A page was requested of a memory manager via
+ * memory_object_data_request for an object using
+ * a MEMORY_OBJECT_COPY_CALL strategy, with the
+ * VM_PROT_WANTS_COPY flag being used to specify
+ * that the page desired is for a copy of the
+ * object, and the memory manager has detected
+ * the page was pushed into a copy of the object
+ * while the kernel was walking the shadow chain
+ * from the copy to the object. This error code
+ * is delivered via memory_object_data_error
+ * and is handled by the kernel (it forces the
+ * kernel to restart the fault). It will not be
+ * seen by users.
+ */
+ Memory_Data_Moved,
+
+ /* A strategic copy was attempted of an object
+ * upon which a quicker copy is now possible.
+ * The caller should retry the copy using
+ * vm_object_copy_quickly. This error code
+ * is seen only by the kernel.
+ */
+ Memory_Restart_Copy,
+
+ /* An argument applied to assert processor set privilege
+ * was not a processor set control port.
+ */
+ Invalid_Processor_Set,
+
+ /* The specified scheduling attributes exceed the thread's
+ * limits.
+ */
+ Policy_Limit,
+
+ /* The specified scheduling policy is not currently
+ * enabled for the processor set.
+ */
+ Invalid_Policy,
+
+ /* The external memory manager failed to initialize the
+ * memory object.
+ */
+ Invalid_Object,
+
+ /* A thread is attempting to wait for an event for which
+ * there is already a waiting thread.
+ */
+ Already_Waiting,
+
+ /* An attempt was made to destroy the default processor
+ * set.
+ */
+ Default_Set,
+
+ /* An attempt was made to fetch an exception port that is
+ * protected, or to abort a thread while processing a
+ * protected exception.
+ */
+ Exception_Protected,
+
+ /* A ledger was required but not supplied.
+ */
+ Invalid_Ledger,
+
+ /* The port was not a memory cache control port.
+ */
+ Invalid_Memory_Control,
+
+ /* An argument supplied to assert security privilege
+ * was not a host security port.
+ */
+ Invalid_Security,
+
+ /* thread_depress_abort was called on a thread which
+ * was not currently depressed.
+ */
+ Not_Depressed,
+
+ /* Object has been terminated and is no longer available
+ */
+ Terminated,
+
+ /* Lock set has been destroyed and is no longer available.
+ */
+ Lock_Set_Destroyed,
+
+ /* The thread holding the lock terminated before releasing
+ * the lock
+ */
+ Lock_Unstable,
+
+ /* The lock is already owned by another thread
+ */
+ Lock_Owned,
+
+ /* The lock is already owned by the calling thread
+ */
+ Lock_Owned_Self,
+
+ /* Semaphore has been destroyed and is no longer available.
+ */
+ Semaphore_Destroyed,
+
+ /* Return from RPC indicating the target server was
+ * terminated before it successfully replied
+ */
+ Rpc_Server_Terminated,
+
+ /* Terminate an orphaned activation.
+ */
+ RPC_Terminate_Orphan,
+
+ /* Allow an orphaned activation to continue executing.
+ */
+ RPC_Continue_Orphan,
+
+ /* Empty thread activation (No thread linked to it)
+ */
+ Not_Supported,
+
+ /* Remote node down or inaccessible.
+ */
+ Node_Down,
+
+ /* A signalled thread was not actually waiting. */
+ Not_Waiting,
+
+ /* Some thread-oriented operation (semaphore_wait) timed out
+ */
+ Operation_Timed_Out,
+
+ /* During a page fault, indicates that the page was rejected
+ * as a result of a signature check.
+ */
+ Codesign_Error,
+
+ /* The requested property cannot be changed at this time.
+ */
+ Policy_Static,
+
+ /* The provided buffer is of insufficient size for the requested data.
+ */
+ Insufficient_Buffer_Size,
+
+ /* Denied by security policy
+ */
+ Denied,
+
+ /* The KC on which the function is operating is missing
+ */
+ Missing_KC,
+
+ /* The KC on which the function is operating is invalid
+ */
+ Invalid_KC,
+
+ /* A search or query operation did not return a result
+ */
+ Not_Found,
+
+ /* Maximum return value allowable
+ */
+ Return_Max = 0x100,
+}
+
+/*
+ * VM allocation flags:
+ *
+ * VM_FLAGS_FIXED
+ * (really the absence of VM_FLAGS_ANYWHERE)
+ * Allocate new VM region at the specified virtual address, if possible.
+ *
+ * VM_FLAGS_ANYWHERE
+ * Allocate new VM region anywhere it would fit in the address space.
+ *
+ * VM_FLAGS_PURGABLE
+ * Create a purgable VM object for that new VM region.
+ *
+ * VM_FLAGS_4GB_CHUNK
+ * The new VM region will be chunked up into 4GB sized pieces.
+ *
+ * VM_FLAGS_NO_PMAP_CHECK
+ * (for DEBUG kernel config only, ignored for other configs)
+ * Do not check that there is no stale pmap mapping for the new VM region.
+ * This is useful for kernel memory allocations at bootstrap when building
+ * the initial kernel address space while some memory is already in use.
+ *
+ * VM_FLAGS_OVERWRITE
+ * The new VM region can replace existing VM regions if necessary
+ * (to be used in combination with VM_FLAGS_FIXED).
+ *
+ * VM_FLAGS_NO_CACHE
+ * Pages brought in to this VM region are placed on the speculative
+ * queue instead of the active queue. In other words, they are not
+ * cached so that they will be stolen first if memory runs low.
+ */
+
+@(private="file")
+LOG2 :: intrinsics.constant_log2
+
+VM_Flag :: enum c.int {
+ Anywhere,
+ Purgable,
+ _4GB_Chunk,
+ Random_Addr,
+ No_Cache,
+ Resilient_Codesign,
+ Resilient_Media,
+ Permanent,
+
+ // NOTE(beau): log 2 of the bit we want in the bit set so we get that bit in
+ // the bit set
+
+ TPRO = LOG2(0x1000),
+ Overwrite = LOG2(0x4000),/* delete any existing mappings first */
+
+ Superpage_Size_Any = LOG2(0x10000),
+ Superpage_Size_2MB = LOG2(0x20000),
+ __Superpage3 = LOG2(0x40000),
+
+ Return_Data_Addr = LOG2(0x100000),
+ Return_4K_Data_Addr = LOG2(0x800000),
+
+ Alias_Mask1 = 24,
+ Alias_Mask2,
+ Alias_Mask3,
+ Alias_Mask4,
+ Alias_Mask5,
+ Alias_Mask6,
+ Alias_Mask7,
+ Alias_Mask8,
+
+ HW = TPRO,
+}
+
+VM_Flags :: distinct bit_set[VM_Flag; c.int]
+VM_FLAGS_FIXED :: VM_Flags{}
+
+/*
+ * VM_FLAGS_SUPERPAGE_MASK
+ * 3 bits that specify whether large pages should be used instead of
+ * base pages (!=0), as well as the requested page size.
+ */
+VM_FLAGS_SUPERPAGE_MASK :: VM_Flags {
+ .Superpage_Size_Any,
+ .Superpage_Size_2MB,
+ .__Superpage3,
+}
+
+// 0xFF000000
+VM_FLAGS_ALIAS_MASK :: VM_Flags {
+ .Alias_Mask1,
+ .Alias_Mask2,
+ .Alias_Mask3,
+ .Alias_Mask4,
+ .Alias_Mask5,
+ .Alias_Mask6,
+ .Alias_Mask7,
+ .Alias_Mask8,
+}
+
+VM_GET_FLAGS_ALIAS :: proc(flags: VM_Flags) -> c.int {
+ return transmute(c.int)(flags & VM_FLAGS_ALIAS_MASK) >> 24
+}
+// NOTE(beau): no need for VM_SET_FLAGS_ALIAS, just mask in things from
+// VM_Flag.Alias_Mask*
+
+/* These are the flags that we accept from user-space */
+VM_FLAGS_USER_ALLOCATE :: VM_Flags {
+ .Anywhere,
+ .Purgable,
+ ._4GB_Chunk,
+ .Random_Addr,
+ .No_Cache,
+ .Permanent,
+ .Overwrite,
+} | VM_FLAGS_FIXED | VM_FLAGS_SUPERPAGE_MASK | VM_FLAGS_ALIAS_MASK
+
+VM_FLAGS_USER_MAP :: VM_Flags {
+ .Return_4K_Data_Addr,
+ .Return_Data_Addr,
+} | VM_FLAGS_USER_ALLOCATE
+
+VM_FLAGS_USER_REMAP :: VM_Flags {
+ .Anywhere,
+ .Random_Addr,
+ .Overwrite,
+ .Return_Data_Addr,
+ .Resilient_Codesign,
+ .Resilient_Media,
+} | VM_FLAGS_FIXED
+
+VM_FLAGS_SUPERPAGE_NONE :: VM_Flags{} /* no superpages, if all bits are 0 */
+
+/*
+ * Protection values, defined as bits within the vm_prot_t type
+ */
+
+VM_Prot :: enum vm_prot_t {
+ Read,
+ Write,
+ Execute,
+}
+
+VM_Prot_Flags :: distinct bit_set[VM_Prot; vm_prot_t]
+
+VM_PROT_NONE :: VM_Prot_Flags{}
+VM_PROT_DEFAULT :: VM_Prot_Flags{.Read, .Write}
+VM_PROT_ALL :: VM_Prot_Flags{.Read, .Write, .Execute}
+
+/*
+ * Enumeration of valid values for vm_inherit_t.
+ */
+
+VM_Inherit :: enum vm_inherit_t {
+ Share,
+ Copy,
+ None,
+ Donate_Copy,
+
+ Default = Copy,
+ Last_Valid = None,
+}
+
+Sync_Policy :: enum sync_policy_t {
+ Fifo,
+ Fixed_Priority,
+ Reversed,
+ Order_Mask,
- semaphore_signal :: proc(semaphore: semaphore_t) -> kern_return_t ---
- semaphore_signal_all :: proc(semaphore: semaphore_t) -> kern_return_t ---
- semaphore_signal_thread :: proc(semaphore: semaphore_t, thread: thread_act_t) -> kern_return_t ---
-
- semaphore_wait :: proc(semaphore: semaphore_t) -> kern_return_t ---
+ Lifo = Fifo | Reversed,
}
mach_vm_trunc_page :: proc(v: u64) -> u64 {
diff --git a/core/sys/info/cpu_intel.odin b/core/sys/info/cpu_intel.odin
index d6fa98507..95b53dda0 100644
--- a/core/sys/info/cpu_intel.odin
+++ b/core/sys/info/cpu_intel.odin
@@ -28,6 +28,23 @@ CPU_Feature :: enum u64 {
ssse3, // Supplemental streaming SIMD extension 3
sse41, // Streaming SIMD extension 4 and 4.1
sse42, // Streaming SIMD extension 4 and 4.2
+
+ avx512bf16, // Vector Neural Network Instructions supporting bfloat16
+ avx512bitalg, // Bit Algorithms
+ avx512bw, // Byte and Word instructions
+ avx512cd, // Conflict Detection instructions
+ avx512dq, // Doubleword and Quadword instructions
+ avx512er, // Exponential and Reciprocal instructions
+ avx512f, // Foundation
+ avx512fp16, // Vector 16-bit float instructions
+ avx512ifma, // Integer Fused Multiply Add
+ avx512pf, // Prefetch instructions
+ avx512vbmi, // Vector Byte Manipulation Instructions
+ avx512vbmi2, // Vector Byte Manipulation Instructions 2
+ avx512vl, // Vector Length extensions
+ avx512vnni, // Vector Neural Network Instructions
+ avx512vp2intersect, // Vector Pair Intersection to a Pair of Mask Registers
+ avx512vpopcntdq, // Vector Population Count for Doubleword and Quadword
}
CPU_Features :: distinct bit_set[CPU_Feature; u64]
@@ -82,9 +99,11 @@ init_cpu_features :: proc "c" () {
//
// See: crbug.com/375968
os_supports_avx := false
+ os_supports_avx512 := false
if .os_xsave in set && is_set(26, ecx1) {
eax, _ := xgetbv(0)
os_supports_avx = is_set(1, eax) && is_set(2, eax)
+ os_supports_avx512 = is_set(5, eax) && is_set(6, eax) && is_set(7, eax)
}
if os_supports_avx {
try_set(&set, .avx, 28, ecx1)
@@ -94,11 +113,37 @@ init_cpu_features :: proc "c" () {
return
}
- _, ebx7, _, _ := cpuid(7, 0)
+ _, ebx7, ecx7, edx7 := cpuid(7, 0)
try_set(&set, .bmi1, 3, ebx7)
if os_supports_avx {
try_set(&set, .avx2, 5, ebx7)
}
+ if os_supports_avx512 {
+ try_set(&set, .avx512f, 16, ebx7)
+ try_set(&set, .avx512dq, 17, ebx7)
+ try_set(&set, .avx512ifma, 21, ebx7)
+ try_set(&set, .avx512pf, 26, ebx7)
+ try_set(&set, .avx512er, 27, ebx7)
+ try_set(&set, .avx512cd, 28, ebx7)
+ try_set(&set, .avx512bw, 30, ebx7)
+
+ // XMM/YMM are also required for 128/256-bit instructions
+ if os_supports_avx {
+ try_set(&set, .avx512vl, 31, ebx7)
+ }
+
+ try_set(&set, .avx512vbmi, 1, ecx7)
+ try_set(&set, .avx512vbmi2, 6, ecx7)
+ try_set(&set, .avx512vnni, 11, ecx7)
+ try_set(&set, .avx512bitalg, 12, ecx7)
+ try_set(&set, .avx512vpopcntdq, 14, ecx7)
+
+ try_set(&set, .avx512vp2intersect, 8, edx7)
+ try_set(&set, .avx512fp16, 23, edx7)
+
+ eax7_1, _, _, _ := cpuid(7, 1)
+ try_set(&set, .avx512bf16, 5, eax7_1)
+ }
try_set(&set, .bmi2, 8, ebx7)
try_set(&set, .erms, 9, ebx7)
try_set(&set, .rdseed, 18, ebx7)
diff --git a/core/sys/info/platform_darwin.odin b/core/sys/info/platform_darwin.odin
index 493f038f0..97a2199ab 100644
--- a/core/sys/info/platform_darwin.odin
+++ b/core/sys/info/platform_darwin.odin
@@ -494,6 +494,10 @@ macos_release_map: map[string]Darwin_To_Release = {
"21G816" = {{21, 6, 0}, "macOS", {"Monterey", {12, 7, 0}}},
"21G920" = {{21, 6, 0}, "macOS", {"Monterey", {12, 7, 1}}},
"21G1974" = {{21, 6, 0}, "macOS", {"Monterey", {12, 7, 2}}},
+ "21H1015" = {{21, 6, 0}, "macOS", {"Monterey", {12, 7, 3}}},
+ "21H1123" = {{21, 6, 0}, "macOS", {"Monterey", {12, 7, 4}}},
+ "21H1222" = {{21, 6, 0}, "macOS", {"Monterey", {12, 7, 5}}},
+ "21H1320" = {{21, 6, 0}, "macOS", {"Monterey", {12, 7, 6}}},
// MacOS Ventura
"22A380" = {{22, 1, 0}, "macOS", {"Ventura", {13, 0, 0}}},
@@ -513,6 +517,15 @@ macos_release_map: map[string]Darwin_To_Release = {
"22G120" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 0}}},
"22G313" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 1}}},
"22G320" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 2}}},
+ "22G436" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 3}}},
+ "22G513" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 4}}},
+ "22G621" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 5}}},
+ "22G630" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 6}}},
+ "22G720" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 7}}},
+ "22G820" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 8}}},
+ "22G830" = {{22, 6, 0}, "macOS", {"Ventura", {13, 6, 9}}},
+ "22H123" = {{22, 6, 0}, "macOS", {"Ventura", {13, 7, 0}}},
+ "22H221" = {{22, 6, 0}, "macOS", {"Ventura", {13, 7, 1}}},
// MacOS Sonoma
"23A344" = {{23, 0, 0}, "macOS", {"Sonoma", {14, 0, 0}}},
@@ -531,9 +544,12 @@ macos_release_map: map[string]Darwin_To_Release = {
"23G80" = {{23, 6, 0}, "macOS", {"Sonoma", {14, 6, 0}}},
"23G93" = {{23, 6, 0}, "macOS", {"Sonoma", {14, 6, 1}}},
"23H124" = {{23, 6, 0}, "macOS", {"Sonoma", {14, 7, 0}}},
+ "23H222" = {{23, 6, 0}, "macOS", {"Sonoma", {14, 7, 1}}},
// MacOS Sequoia
"24A335" = {{24, 0, 0}, "macOS", {"Sequoia", {15, 0, 0}}},
+ "24A348" = {{24, 0, 0}, "macOS", {"Sequoia", {15, 0, 1}}},
+ "24B83" = {{24, 1, 0}, "macOS", {"Sequoia", {15, 1, 0}}},
}
@(private)
diff --git a/core/sys/linux/bits.odin b/core/sys/linux/bits.odin
index 8a4a6dd7a..9ce2e206e 100644
--- a/core/sys/linux/bits.odin
+++ b/core/sys/linux/bits.odin
@@ -152,66 +152,43 @@ Errno :: enum i32 {
RDONLY flag is not present, because it has the value of 0, i.e. it is the
default, unless WRONLY or RDWR is specified.
*/
-when ODIN_ARCH != .arm64 && ODIN_ARCH != .arm32 {
- Open_Flags_Bits :: enum {
- WRONLY = 0,
- RDWR = 1,
- CREAT = 6,
- EXCL = 7,
- NOCTTY = 8,
- TRUNC = 9,
- APPEND = 10,
- NONBLOCK = 11,
- DSYNC = 12,
- ASYNC = 13,
- DIRECT = 14,
- LARGEFILE = 15,
- DIRECTORY = 16,
- NOFOLLOW = 17,
- NOATIME = 18,
- CLOEXEC = 19,
- PATH = 21,
- }
- // https://github.com/torvalds/linux/blob/7367539ad4b0f8f9b396baf02110962333719a48/include/uapi/asm-generic/fcntl.h#L19
- #assert(1 << uint(Open_Flags_Bits.WRONLY) == 0o0000000_1)
- #assert(1 << uint(Open_Flags_Bits.RDWR) == 0o0000000_2)
- #assert(1 << uint(Open_Flags_Bits.CREAT) == 0o00000_100)
- #assert(1 << uint(Open_Flags_Bits.EXCL) == 0o00000_200)
- #assert(1 << uint(Open_Flags_Bits.NOCTTY) == 0o00000_400)
- #assert(1 << uint(Open_Flags_Bits.TRUNC) == 0o0000_1000)
- #assert(1 << uint(Open_Flags_Bits.APPEND) == 0o0000_2000)
- #assert(1 << uint(Open_Flags_Bits.NONBLOCK) == 0o0000_4000)
- #assert(1 << uint(Open_Flags_Bits.DSYNC) == 0o000_10000)
- #assert(1 << uint(Open_Flags_Bits.ASYNC) == 0o000_20000)
- #assert(1 << uint(Open_Flags_Bits.DIRECT) == 0o000_40000)
- #assert(1 << uint(Open_Flags_Bits.LARGEFILE) == 0o00_100000)
- #assert(1 << uint(Open_Flags_Bits.DIRECTORY) == 0o00_200000)
- #assert(1 << uint(Open_Flags_Bits.NOFOLLOW) == 0o00_400000)
- #assert(1 << uint(Open_Flags_Bits.NOATIME) == 0o0_1000000)
- #assert(1 << uint(Open_Flags_Bits.CLOEXEC) == 0o0_2000000)
- #assert(1 << uint(Open_Flags_Bits.PATH) == 0o_10000000)
-
-} else {
- Open_Flags_Bits :: enum {
- WRONLY = 0,
- RDWR = 1,
- CREAT = 6,
- EXCL = 7,
- NOCTTY = 8,
- TRUNC = 9,
- APPEND = 10,
- NONBLOCK = 11,
- DSYNC = 12,
- ASYNC = 13,
- DIRECTORY = 14,
- NOFOLLOW = 15,
- DIRECT = 16,
- LARGEFILE = 17,
- NOATIME = 18,
- CLOEXEC = 19,
- PATH = 21,
- }
-}
+Open_Flags_Bits :: enum {
+ WRONLY = 0,
+ RDWR = 1,
+ CREAT = 6,
+ EXCL = 7,
+ NOCTTY = 8,
+ TRUNC = 9,
+ APPEND = 10,
+ NONBLOCK = 11,
+ DSYNC = 12,
+ ASYNC = 13,
+ DIRECT = 14,
+ LARGEFILE = 15,
+ DIRECTORY = 16,
+ NOFOLLOW = 17,
+ NOATIME = 18,
+ CLOEXEC = 19,
+ PATH = 21,
+}
+// https://github.com/torvalds/linux/blob/7367539ad4b0f8f9b396baf02110962333719a48/include/uapi/asm-generic/fcntl.h#L19
+#assert(1 << uint(Open_Flags_Bits.WRONLY) == 0o0000000_1)
+#assert(1 << uint(Open_Flags_Bits.RDWR) == 0o0000000_2)
+#assert(1 << uint(Open_Flags_Bits.CREAT) == 0o00000_100)
+#assert(1 << uint(Open_Flags_Bits.EXCL) == 0o00000_200)
+#assert(1 << uint(Open_Flags_Bits.NOCTTY) == 0o00000_400)
+#assert(1 << uint(Open_Flags_Bits.TRUNC) == 0o0000_1000)
+#assert(1 << uint(Open_Flags_Bits.APPEND) == 0o0000_2000)
+#assert(1 << uint(Open_Flags_Bits.NONBLOCK) == 0o0000_4000)
+#assert(1 << uint(Open_Flags_Bits.DSYNC) == 0o000_10000)
+#assert(1 << uint(Open_Flags_Bits.ASYNC) == 0o000_20000)
+#assert(1 << uint(Open_Flags_Bits.DIRECT) == 0o000_40000)
+#assert(1 << uint(Open_Flags_Bits.LARGEFILE) == 0o00_100000)
+#assert(1 << uint(Open_Flags_Bits.DIRECTORY) == 0o00_200000)
+#assert(1 << uint(Open_Flags_Bits.NOFOLLOW) == 0o00_400000)
+#assert(1 << uint(Open_Flags_Bits.NOATIME) == 0o0_1000000)
+#assert(1 << uint(Open_Flags_Bits.CLOEXEC) == 0o0_2000000)
+#assert(1 << uint(Open_Flags_Bits.PATH) == 0o_10000000)
/*
Bits for FD_Flags bitset
@@ -542,6 +519,36 @@ Fd_Poll_Events_Bits :: enum {
RDHUP = 13,
}
+Inotify_Init_Bits :: enum {
+ NONBLOCK = 11,
+ CLOEXEC = 19,
+}
+
+Inotify_Event_Bits :: enum u32 {
+ ACCESS = 0,
+ MODIFY = 1,
+ ATTRIB = 2,
+ CLOSE_WRITE = 3,
+ CLOSE_NOWRITE = 4,
+ OPEN = 5,
+ MOVED_FROM = 6,
+ MOVED_TO = 7,
+ CREATE = 8,
+ DELETE = 9,
+ DELETE_SELF = 10,
+ MOVE_SELF = 11,
+ UNMOUNT = 13,
+ Q_OVERFLOW = 14,
+ IGNORED = 15,
+ ONLYDIR = 24,
+ DONT_FOLLOW = 25,
+ EXCL_UNLINK = 26,
+ MASK_CREATE = 28,
+ MASK_ADD = 29,
+ ISDIR = 30,
+ ONESHOT = 31,
+}
+
/*
Bits for Mem_Protection bitfield
*/
diff --git a/core/sys/linux/constants.odin b/core/sys/linux/constants.odin
index 129444d0f..b3bbcafb3 100644
--- a/core/sys/linux/constants.odin
+++ b/core/sys/linux/constants.odin
@@ -135,6 +135,31 @@ STATX_BASIC_STATS :: Statx_Mask {
.BLOCKS,
}
+IN_ALL_EVENTS :: Inotify_Event_Mask {
+ .ACCESS,
+ .MODIFY,
+ .ATTRIB,
+ .CLOSE_WRITE,
+ .CLOSE_NOWRITE,
+ .OPEN,
+ .MOVED_FROM,
+ .MOVED_TO,
+ .CREATE,
+ .DELETE,
+ .DELETE_SELF,
+ .MOVE_SELF,
+}
+
+IN_CLOSE :: Inotify_Event_Mask {
+ .CLOSE_WRITE,
+ .CLOSE_NOWRITE,
+}
+
+IN_MOVE :: Inotify_Event_Mask {
+ .MOVED_FROM,
+ .MOVED_TO,
+}
+
/*
Tell `shmget` to create a new key
*/
diff --git a/core/sys/linux/sys.odin b/core/sys/linux/sys.odin
index c5894d78b..690902f07 100644
--- a/core/sys/linux/sys.odin
+++ b/core/sys/linux/sys.odin
@@ -2536,11 +2536,30 @@ waitid :: proc "contextless" (id_type: Id_Type, id: Id, sig_info: ^Sig_Info, opt
// TODO(flysand): ioprio_get
-// TODO(flysand): inotify_init
+inotify_init :: proc "contextless" () -> (Fd, Errno) {
+ when ODIN_ARCH == .arm64 || ODIN_ARCH == .riscv64 {
+ ret := syscall(SYS_inotify_init1, 0)
+ return errno_unwrap(ret, Fd)
+ } else {
+ ret := syscall(SYS_inotify_init)
+ return errno_unwrap(ret, Fd)
+ }
+}
-// TODO(flysand): inotify_add_watch
+inotify_init1 :: proc "contextless" (flags: Inotify_Init_Flags) -> (Fd, Errno) {
+ ret := syscall(SYS_inotify_init1, transmute(i32)flags)
+ return errno_unwrap(ret, Fd)
+}
+
+inotify_add_watch :: proc "contextless" (fd: Fd, pathname: cstring, mask: Inotify_Event_Mask) -> (Wd, Errno) {
+ ret := syscall(SYS_inotify_add_watch, fd, transmute(uintptr) pathname, transmute(u32) mask)
+ return errno_unwrap(ret, Wd)
+}
-// TODO(flysand): inotify_rm_watch
+inotify_rm_watch :: proc "contextless" (fd: Fd, wd: Wd) -> (Errno) {
+ ret := syscall(SYS_inotify_rm_watch, fd, wd)
+ return Errno(-ret)
+}
// TODO(flysand): migrate_pages
diff --git a/core/sys/linux/types.odin b/core/sys/linux/types.odin
index 0e5b8218b..42d5cc988 100644
--- a/core/sys/linux/types.odin
+++ b/core/sys/linux/types.odin
@@ -31,6 +31,11 @@ Id :: distinct uint
Fd :: distinct i32
/*
+ Represents a watch descriptor.
+*/
+Wd :: distinct i32
+
+/*
Type for PID file descriptors.
*/
Pid_FD :: distinct i32
@@ -343,6 +348,18 @@ Poll_Fd :: struct {
revents: Fd_Poll_Events,
}
+Inotify_Init_Flags :: bit_set[Inotify_Init_Bits; i32]
+
+Inotify_Event :: struct {
+ wd: Wd,
+ mask: Inotify_Event_Mask,
+ cookie: u32,
+ len: u32,
+ name: [0]u8,
+}
+
+Inotify_Event_Mask :: bit_set[Inotify_Event_Bits; u32]
+
/*
Specifies protection for memory pages.
*/
@@ -1136,6 +1153,12 @@ when ODIN_ARCH == .arm32 {
eflags: uint,
rsp: uint,
ss: uint,
+ fs_base: uint,
+ gs_base: uint,
+ ds: uint,
+ es: uint,
+ fs: uint,
+ gs: uint,
}
// All floating point state
_Arch_User_FP_Regs :: struct {
diff --git a/core/sys/posix/arpa_inet.odin b/core/sys/posix/arpa_inet.odin
index 7e950c4be..d3592dd80 100644
--- a/core/sys/posix/arpa_inet.odin
+++ b/core/sys/posix/arpa_inet.odin
@@ -1,3 +1,4 @@
+#+build darwin, linux, freebsd, openbsd, netbsd
package posix
import "core:c"
diff --git a/core/sys/posix/dirent.odin b/core/sys/posix/dirent.odin
index bbb5416c5..bf32be8cf 100644
--- a/core/sys/posix/dirent.odin
+++ b/core/sys/posix/dirent.odin
@@ -1,3 +1,4 @@
+#+build darwin, linux, freebsd, openbsd, netbsd
package posix
import "core:c"
@@ -29,12 +30,12 @@ foreign lib {
panic(string(posix.strerror(posix.errno())))
}
defer posix.free(list)
-
+
entries := list[:ret]
- for entry in entries {
- log.info(entry)
- posix.free(entry)
- }
+ for entry in entries {
+ log.info(entry)
+ posix.free(entry)
+ }
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/scandir.html ]]
*/
@@ -54,15 +55,6 @@ foreign lib {
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
@@ -89,16 +81,16 @@ foreign lib {
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)
+ 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 ]]
@@ -160,11 +152,36 @@ when ODIN_OS == .NetBSD {
@(private) LSCANDIR :: "__scandir30"
@(private) LOPENDIR :: "__opendir30"
@(private) LREADDIR :: "__readdir30"
+
+ /*
+ Return a file descriptor referring to the same directory as the dirp argument.
+
+ [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/dirfd.html ]]
+ */
+ dirfd :: proc "c" (dirp: DIR) -> FD {
+ _dirdesc :: struct {
+ dd_fd: FD,
+
+ // more stuff...
+ }
+
+ return (^_dirdesc)(dirp).dd_fd
+ }
+
} else {
@(private) LALPHASORT :: "alphasort" + INODE_SUFFIX
@(private) LSCANDIR :: "scandir" + INODE_SUFFIX
@(private) LOPENDIR :: "opendir" + INODE_SUFFIX
@(private) LREADDIR :: "readdir" + INODE_SUFFIX
+
+ foreign lib {
+ /*
+ Return a file descriptor referring to the same directory as the dirp argument.
+
+ [[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/dirfd.html ]]
+ */
+ dirfd :: proc(dirp: DIR) -> FD ---
+ }
}
when ODIN_OS == .Darwin {
@@ -193,13 +210,21 @@ when ODIN_OS == .Darwin {
} 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 */
+ 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")
+} else when ODIN_OS == .Linux {
+
+ dirent :: struct {
+ d_ino: u64, /* [PSX] file number of entry */
+ d_off: i64, /* directory offset of the next entry */
+ d_reclen: u16, /* length of this record */
+ d_type: D_Type, /* file type */
+ d_name: [256]c.char `fmt:"s,0"`, /* [PSX] entry name */
+ }
+
}
diff --git a/core/sys/posix/dlfcn.odin b/core/sys/posix/dlfcn.odin
index 0ddc41337..e84b29d79 100644
--- a/core/sys/posix/dlfcn.odin
+++ b/core/sys/posix/dlfcn.odin
@@ -1,3 +1,4 @@
+#+build darwin, linux, freebsd, openbsd, netbsd
package posix
import "core:c"
@@ -111,7 +112,14 @@ when ODIN_OS == .Darwin {
RTLD_LOCAL :: RTLD_Flags{RTLD_Flag_Bits(log2(_RTLD_LOCAL))}
-} else {
- #panic("posix is unimplemented for the current target")
+} else when ODIN_OS == .Linux {
+
+ RTLD_LAZY :: 0x001
+ RTLD_NOW :: 0x002
+ RTLD_GLOBAL :: 0x100
+
+ _RTLD_LOCAL :: 0
+ RTLD_LOCAL :: RTLD_Flags{}
+
}
diff --git a/core/sys/posix/errno.odin b/core/sys/posix/errno.odin
index 4ef10aadf..9bc77f12e 100644
--- a/core/sys/posix/errno.odin
+++ b/core/sys/posix/errno.odin
@@ -1,3 +1,4 @@
+#+build windows, darwin, linux, freebsd, openbsd, netbsd
package posix
import "core:c"
@@ -141,7 +142,7 @@ when ODIN_OS == .Darwin {
EMLINK :: 31
EPIPE :: 32
EAGAIN :: 35
- EWOULDBLOCK :: 35
+ EWOULDBLOCK :: EAGAIN
EINPROGRESS :: 36
EALREADY :: 37
ENOTSOCK :: 38
@@ -151,7 +152,7 @@ when ODIN_OS == .Darwin {
ENOPROTOOPT :: 42
EPROTONOSUPPORT :: 43
ENOTSUP :: 45
- EOPNOTSUPP :: 45
+ EOPNOTSUPP :: ENOTSUP
EAFNOSUPPORT :: 47
EADDRINUSE :: 48
EADDRNOTAVAIL :: 49
@@ -220,7 +221,7 @@ when ODIN_OS == .Darwin {
EMLINK :: 31
EPIPE :: 32
EAGAIN :: 35
- EWOULDBLOCK :: 35
+ EWOULDBLOCK :: EAGAIN
EINPROGRESS :: 36
EALREADY :: 37
ENOTSOCK :: 38
@@ -230,7 +231,7 @@ when ODIN_OS == .Darwin {
ENOPROTOOPT :: 42
EPROTONOSUPPORT :: 43
ENOTSUP :: 45
- EOPNOTSUPP :: 45
+ EOPNOTSUPP :: ENOTSUP
EAFNOSUPPORT :: 47
EADDRINUSE :: 48
EADDRNOTAVAIL :: 49
@@ -301,7 +302,7 @@ when ODIN_OS == .Darwin {
EMLINK :: 31
EPIPE :: 32
EAGAIN :: 35
- EWOULDBLOCK :: 35
+ EWOULDBLOCK :: EAGAIN
EINPROGRESS :: 36
EALREADY :: 37
ENOTSOCK :: 38
@@ -311,7 +312,7 @@ when ODIN_OS == .Darwin {
ENOPROTOOPT :: 42
EPROTONOSUPPORT :: 43
ENOTSUP :: 45
- EOPNOTSUPP :: 45
+ EOPNOTSUPP :: ENOTSUP
EAFNOSUPPORT :: 47
EADDRINUSE :: 48
EADDRNOTAVAIL :: 49
@@ -367,7 +368,173 @@ when ODIN_OS == .Darwin {
ETIME :: -1
}
-} else {
- #panic("posix is unimplemented for the current target")
+} else when ODIN_OS == .Linux {
+ EPERM :: 1
+ ENOENT :: 2
+ ESRCH :: 3
+ EINTR :: 4
+ EIO :: 5
+ ENXIO :: 6
+ E2BIG :: 7
+ ENOEXEC :: 8
+ EBADF :: 9
+ ECHILD :: 10
+ EAGAIN :: 11
+ EWOULDBLOCK :: EAGAIN
+ 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
+
+ EDEADLK :: 35
+ ENAMETOOLONG :: 36
+ ENOLCK :: 37
+ ENOSYS :: 38
+ ENOTEMPTY :: 39
+ ELOOP :: 40
+ ENOMSG :: 42
+ EIDRM :: 43
+
+ ENOSTR :: 60
+ ENODATA :: 61
+ ETIME :: 62
+ ENOSR :: 63
+
+ ENOLINK :: 67
+
+ EPROTO :: 71
+ EMULTIHOP :: 72
+ EBADMSG :: 74
+ EOVERFLOW :: 75
+
+ ENOTSOCK :: 88
+ EDESTADDRREQ :: 89
+ EMSGSIZE :: 90
+ EPROTOTYPE :: 91
+ ENOPROTOOPT :: 92
+ EPROTONOSUPPORT :: 93
+
+ EOPNOTSUPP :: 95
+ ENOTSUP :: EOPNOTSUPP
+ EAFNOSUPPORT :: 97
+ EADDRINUSE :: 98
+ EADDRNOTAVAIL :: 99
+ ENETDOWN :: 100
+ ENETUNREACH :: 101
+ ENETRESET :: 102
+ ECONNABORTED :: 103
+ ECONNRESET :: 104
+ ENOBUFS :: 105
+ EISCONN :: 106
+ ENOTCONN :: 107
+
+ ETIMEDOUT :: 110
+ ECONNREFUSED :: 111
+
+ EHOSTUNREACH :: 113
+ EALREADY :: 114
+ EINPROGRESS :: 115
+ ESTALE :: 116
+
+ EDQUOT :: 122
+ ECANCELED :: 125
+
+ EOWNERDEAD :: 130
+ ENOTRECOVERABLE :: 131
+} 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
}
diff --git a/core/sys/posix/fcntl.odin b/core/sys/posix/fcntl.odin
index ca030a9a5..d948af600 100644
--- a/core/sys/posix/fcntl.odin
+++ b/core/sys/posix/fcntl.odin
@@ -1,3 +1,4 @@
+#+build linux, darwin, openbsd, freebsd, netbsd
package posix
import "core:c"
@@ -92,9 +93,6 @@ Lock_Type :: enum c.short {
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),
@@ -107,11 +105,11 @@ O_Flag_Bits :: enum c.int {
// 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),
+ NOFOLLOW = log2(O_NOFOLLOW),
// 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
+ // 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,
@@ -123,7 +121,8 @@ O_Flag_Bits :: enum c.int {
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
+
+ // 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,
@@ -135,11 +134,10 @@ O_Flag_Bits :: enum c.int {
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 {
@@ -152,8 +150,8 @@ 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
+ off_t :: distinct c.int64_t
+ pid_t :: distinct c.int32_t
F_DUPFD :: 0
F_DUPFD_CLOEXEC :: 67
@@ -178,7 +176,7 @@ when ODIN_OS == .Darwin {
O_DIRECTORY :: 0x00100000
O_EXCL :: 0x00000800
O_NOCTTY :: 0x00020000
- O_NOFOLOW :: 0x00000100
+ O_NOFOLLOW :: 0x00000100
O_TRUNC :: 0x00000400
_O_TTY_INIT :: 0
@@ -189,16 +187,16 @@ when ODIN_OS == .Darwin {
O_NONBLOCK :: 0x00000004
O_SYNC :: 0x0080
- _O_RSYNC :: 0
- O_RSYNC :: O_Flags{}
+ _O_RSYNC :: 0
+ O_RSYNC :: O_Flags{}
- O_EXEC :: 0x40000000
- O_RDONLY :: 0
- O_RDWR :: 0x0002
- O_WRONLY :: 0x0001
+ O_EXEC :: 0x40000000
+ O_RDONLY :: 0
+ O_RDWR :: 0x0002
+ O_WRONLY :: 0x0001
_O_SEARCH :: O_EXEC | O_DIRECTORY
- O_SEARCH :: O_Flags{ .EXEC, .DIRECTORY }
+ O_SEARCH :: O_Flags{.EXEC, .DIRECTORY}
AT_FDCWD: FD: -2
@@ -217,8 +215,8 @@ when ODIN_OS == .Darwin {
} else when ODIN_OS == .FreeBSD {
- off_t :: distinct c.int64_t
- pid_t :: distinct c.int32_t
+ off_t :: distinct c.int64_t
+ pid_t :: distinct c.int32_t
F_DUPFD :: 0
F_DUPFD_CLOEXEC :: 17
@@ -243,7 +241,7 @@ when ODIN_OS == .Darwin {
O_DIRECTORY :: 0x00020000
O_EXCL :: 0x0800
O_NOCTTY :: 0x8000
- O_NOFOLOW :: 0x0100
+ O_NOFOLLOW :: 0x0100
O_TRUNC :: 0x0400
_O_TTY_INIT :: 0x00080000
@@ -256,10 +254,10 @@ when ODIN_OS == .Darwin {
_O_RSYNC :: 0
O_RSYNC :: O_Flags{} // NOTE: not defined in headers
- O_EXEC :: 0x00040000
- O_RDONLY :: 0
- O_RDWR :: 0x0002
- O_WRONLY :: 0x0001
+ O_EXEC :: 0x00040000
+ O_RDONLY :: 0
+ O_RDWR :: 0x0002
+ O_WRONLY :: 0x0001
_O_SEARCH :: O_EXEC
O_SEARCH :: O_Flags{ .EXEC }
@@ -282,8 +280,8 @@ when ODIN_OS == .Darwin {
} else when ODIN_OS == .NetBSD {
- off_t :: distinct c.int64_t
- pid_t :: distinct c.int32_t
+ off_t :: distinct c.int64_t
+ pid_t :: distinct c.int32_t
F_DUPFD :: 0
F_DUPFD_CLOEXEC :: 12
@@ -308,7 +306,7 @@ when ODIN_OS == .Darwin {
O_DIRECTORY :: 0x0020000
O_EXCL :: 0x0800
O_NOCTTY :: 0x8000
- O_NOFOLOW :: 0x0100
+ O_NOFOLLOW :: 0x0100
O_TRUNC :: 0x0400
_O_TTY_INIT :: 0
@@ -319,14 +317,14 @@ when ODIN_OS == .Darwin {
O_NONBLOCK :: 0x0004
O_SYNC :: 0x0080
- _O_RSYNC :: 0x0002
- O_RSYNC :: O_Flags{O_Flag_Bits(log2(_O_RSYNC))}
+ _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_EXEC :: 0x04000000
+ O_RDONLY :: 0
+ O_RDWR :: 0x0002
+ O_WRONLY :: 0x0001
_O_SEARCH :: 0x00800000
O_SEARCH :: O_Flags{O_Flag_Bits(log2(_O_SEARCH))}
@@ -347,8 +345,8 @@ when ODIN_OS == .Darwin {
}
} else when ODIN_OS == .OpenBSD {
- off_t :: distinct c.int64_t
- pid_t :: distinct c.int32_t
+ off_t :: distinct c.int64_t
+ pid_t :: distinct c.int32_t
F_DUPFD :: 0
F_DUPFD_CLOEXEC :: 10
@@ -373,7 +371,7 @@ when ODIN_OS == .Darwin {
O_DIRECTORY :: 0x20000
O_EXCL :: 0x0800
O_NOCTTY :: 0x8000
- O_NOFOLOW :: 0x0100
+ O_NOFOLLOW :: 0x0100
O_TRUNC :: 0x0400
_O_TTY_INIT :: 0
@@ -384,13 +382,13 @@ when ODIN_OS == .Darwin {
O_NONBLOCK :: 0x0004
O_SYNC :: 0x0080
- _O_RSYNC :: O_SYNC
- O_RSYNC :: O_Flags{ .SYNC }
+ _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_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
@@ -410,6 +408,70 @@ when ODIN_OS == .Darwin {
l_whence: c.short, /* [PSX] flag (Whence) of starting offset */
}
-} else {
- #panic("posix is unimplemented for the current target")
+} else when ODIN_OS == .Linux {
+
+ off_t :: distinct c.int64_t
+ pid_t :: distinct c.int
+
+ F_DUPFD :: 0
+ F_GETFD :: 1
+ F_SETFD :: 2
+ F_GETFL :: 3
+ F_SETFL :: 4
+ F_GETLK :: 5
+ F_SETLK :: 6
+ F_SETLKW :: 7
+ F_SETOWN :: 8
+ F_GETOWN :: 9
+ F_RDLCK :: 0
+ F_UNLCK :: 2
+ F_WRLCK :: 1
+
+ F_DUPFD_CLOEXEC :: 1030
+
+ FD_CLOEXEC :: 1
+
+ O_CREAT :: 0o0_000_100
+ O_EXCL :: 0o0_000_200
+ O_NOCTTY :: 0o0_000_400
+ O_TRUNC :: 0o0_001_000
+ O_DIRECTORY :: 0o0_200_000
+ O_NOFOLLOW :: 0o0_400_000
+ O_CLOEXEC :: 0o2_000_000
+
+ _O_TTY_INIT :: 0
+ O_TTY_INIT :: O_Flags{}
+
+ O_APPEND :: 0o0_002_000
+ O_NONBLOCK :: 0o0_004_000
+ O_DSYNC :: 0o0_010_000
+ O_SYNC :: 0o4_010_000
+
+ _O_RSYNC :: 0
+ O_RSYNC :: O_Flags{}
+
+ O_EXEC :: 0x04000000 // NOTE: not defined in the headers
+
+ O_RDONLY :: 0
+ O_WRONLY :: 0o1
+ O_RDWR :: 0o2
+
+ _O_SEARCH :: 0
+ O_SEARCH :: O_Flags{}
+
+ AT_FDCWD: FD: -100
+
+ AT_EACCESS :: 0x200
+ AT_SYMLINK_NOFOLLOW :: 0x100
+ AT_SYMLINK_FOLLOW :: 0x400
+ 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. */
+ }
+
}
diff --git a/core/sys/posix/fnmatch.odin b/core/sys/posix/fnmatch.odin
index 9e54972e7..2d582705c 100644
--- a/core/sys/posix/fnmatch.odin
+++ b/core/sys/posix/fnmatch.odin
@@ -1,3 +1,4 @@
+#+build darwin, linux, openbsd, freebsd, netbsd
package posix
import "core:c"
@@ -53,6 +54,12 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
FNM_PERIOD :: 0x04
FNM_NOESCAPE :: 0x01
-} else {
- #panic("posix is unimplemented for the current target")
+} else when ODIN_OS == .Linux {
+
+ FNM_NOMATCH :: 1
+
+ FNM_PATHNAME :: 0x01
+ FNM_NOESCAPE :: 0x02
+ FNM_PERIOD :: 0x04
+
}
diff --git a/core/sys/posix/glob.odin b/core/sys/posix/glob.odin
index 4f41d83db..7c8009a59 100644
--- a/core/sys/posix/glob.odin
+++ b/core/sys/posix/glob.odin
@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
package posix
import "core:c"
@@ -112,7 +113,7 @@ when ODIN_OS == .Darwin {
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_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 */
@@ -144,7 +145,7 @@ when ODIN_OS == .Darwin {
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_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 */
@@ -174,6 +175,33 @@ when ODIN_OS == .Darwin {
GLOB_NOMATCH :: -3
GLOB_NOSPACE :: -1
-} else {
- #panic("posix is unimplemented for the current target")
+} else when ODIN_OS == .Linux {
+
+ glob_t :: struct {
+ gl_pathc: c.size_t, /* [PSX] count of paths matched by pattern */
+ gl_pathv: [^]cstring `fmt:"v,gl_pathc"`, /* [PSX] pointer to list of matched pathnames */
+ 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 */
+
+ // Non-standard alternate file system access functions:
+
+ 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_ERR :: 1 << 0
+ GLOB_MARK :: 1 << 1
+ GLOB_NOSORT :: 1 << 2
+ GLOB_DOOFFS :: 1 << 3
+ GLOB_NOCHECK :: 1 << 4
+ GLOB_APPEND :: 1 << 5
+ GLOB_NOESCAPE :: 1 << 6
+
+ GLOB_NOSPACE :: 1
+ GLOB_ABORTED :: 2
+ GLOB_NOMATCH :: 3
+
}
diff --git a/core/sys/posix/grp.odin b/core/sys/posix/grp.odin
index c8a39de6a..956ed148b 100644
--- a/core/sys/posix/grp.odin
+++ b/core/sys/posix/grp.odin
@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
package posix
import "core:c"
@@ -114,7 +115,7 @@ foreign lib {
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 {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
gid_t :: distinct c.uint32_t
@@ -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")
}
diff --git a/core/sys/posix/iconv.odin b/core/sys/posix/iconv.odin
index 59248890f..f7447be9e 100644
--- a/core/sys/posix/iconv.odin
+++ b/core/sys/posix/iconv.odin
@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
package posix
import "core:c"
diff --git a/core/sys/posix/langinfo.odin b/core/sys/posix/langinfo.odin
index 24ecc917a..3c001aee0 100644
--- a/core/sys/posix/langinfo.odin
+++ b/core/sys/posix/langinfo.odin
@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
package posix
import "core:c"
@@ -238,7 +239,7 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD {
ABDAY_4 :: 16
ABDAY_5 :: 17
ABDAY_6 :: 18
- ABDAY_7 :: 19
+ ABDAY_7 :: 19
MON_1 :: 20
MON_2 :: 21
@@ -278,7 +279,91 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD {
YESEXPR :: 47
NOEXPR :: 49
- CRNCYSTR :: 50
+ CRNCYSTR :: 50
+
+} else when ODIN_OS == .Linux {
+
+ // NOTE: declared with `_t` so we can enumerate the real `nl_info`.
+ nl_item_t :: distinct c.int
+
+ // NOTE: All these values are set in an enum on the Linux implementation.
+ // Some depend on locale.h contants (bits/locale.h to be precise).
+
+ // NOTE: ABDAY_1 is set to LC_TIME << 16 (LC_TIME is 2) on the enum group of
+ // the Linux implementation.
+ ABDAY_1 :: 0x20_000
+ ABDAY_2 :: 0x20_001
+ ABDAY_3 :: 0x20_002
+ ABDAY_4 :: 0x20_003
+ ABDAY_5 :: 0x20_004
+ ABDAY_6 :: 0x20_005
+ ABDAY_7 :: 0x20_006
+
+ DAY_1 :: 0x20_007
+ DAY_2 :: 0x20_008
+ DAY_3 :: 0x20_009
+ DAY_4 :: 0x20_00A
+ DAY_5 :: 0x20_00B
+ DAY_6 :: 0x20_00C
+ DAY_7 :: 0x20_00D
+
+ ABMON_1 :: 0x20_00E
+ ABMON_2 :: 0x20_010
+ ABMON_3 :: 0x20_011
+ ABMON_4 :: 0x20_012
+ ABMON_5 :: 0x20_013
+ ABMON_6 :: 0x20_014
+ ABMON_7 :: 0x20_015
+ ABMON_8 :: 0x20_016
+ ABMON_9 :: 0x20_017
+ ABMON_10 :: 0x20_018
+ ABMON_11 :: 0x20_019
+ ABMON_12 :: 0x20_01A
+
+ MON_1 :: 0x20_01B
+ MON_2 :: 0x20_01C
+ MON_3 :: 0x20_01D
+ MON_4 :: 0x20_01E
+ MON_5 :: 0x20_020
+ MON_6 :: 0x20_021
+ MON_7 :: 0x20_022
+ MON_8 :: 0x20_023
+ MON_9 :: 0x20_024
+ MON_10 :: 0x20_025
+ MON_11 :: 0x20_026
+ MON_12 :: 0x20_027
+
+ AM_STR :: 0x20_028
+ PM_STR :: 0x20_029
+
+ D_T_FMT :: 0x20_02A
+ D_FMT :: 0x20_02B
+ T_FMT :: 0x20_02C
+ T_FMT_AMPM :: 0x20_02D
+
+ ERA :: 0x20_02E
+ ERA_D_FMT :: 0x20_030
+ ALT_DIGITS :: 0x20_031
+ ERA_D_T_FMT :: 0x20_032
+ ERA_T_FMT :: 0x20_033
+
+ // NOTE: CODESET is the 16th member of the enum group starting with value
+ // LC_CTYPE << 16, LC_CTYPE is 0.
+ CODESET :: 0x0F
+
+ // NOTE: CRNCYSTR is the 16th member of the enum group starting with value
+ // LC_MONETARY << 16, LC_MONETARY is 4.
+ CRNCYSTR :: 0x40_00F
+
+ // NOTE: RADIXCHAR is the 1st member of the enum group starting with value
+ // LC_NUMERIC << 16, LC_NUMERIC is 1.
+ RADIXCHAR :: 0x10_000
+ THOUSEP :: 0x10_001
+
+ // NOTE: YESEXPR is the 1st member of the enum group starting with value
+ // LC_MESSAGES << 16, LC_MESSAGES is 5.
+ YESEXPR :: 0x50_000
+ NOEXPR :: 0x50_001
} else {
#panic("posix is unimplemented for the current target")
diff --git a/core/sys/posix/libgen.odin b/core/sys/posix/libgen.odin
index 99506797e..69176a557 100644
--- a/core/sys/posix/libgen.odin
+++ b/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"
+}
diff --git a/core/sys/posix/limits.odin b/core/sys/posix/limits.odin
index 7bb561215..6680986ad 100644
--- a/core/sys/posix/limits.odin
+++ b/core/sys/posix/limits.odin
@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
package posix
// limits.h - implementation-defined constants
@@ -454,6 +455,99 @@ when ODIN_OS == .Darwin {
NL_TEXTMAX :: 255
NZERO :: 20
-} else {
- #panic("posix is unimplemented for the current target")
+} else when ODIN_OS == .Linux {
+
+ // 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 :: 131_072
+ // ATEXIT_MAX :: sysconf(._ATEXIT_MAX)
+ // CHILD_MAX :: sysconf(._POSIX_ARG_MAX)
+ // DELAYTIMER_MAX :: sysconf(._DELAYTIMER_MAX)
+ // HOST_NAME_MAX :: sysconf(._HOST_NAME_MAX)
+ // IOV_MAX :: sysconf(._XOPEN_IOV_MAX)
+ // 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 :: sysconf(._PAGE_SIZE)
+ PTHREAD_DESTRUCTOR_ITERATIONS :: 4
+ // PTHREAD_KEYS_MAX :: sysconf(._PTHREAD_KEYS_MAX)
+ // PTHREAD_STACK_MIN :: sysconf(._PTHREAD_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(._SYSLOOP_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 :: 127
+ MAX_CANON :: 255
+ MAX_INPUT :: 255
+ NAME_MAX :: 255
+ PATH_MAX :: 4096
+ PIPE_BUF :: 4096
+ // POSIX_ALLOC_SIZE_MIN :: sysconf(._POSIX_ALLOC_SIZE_MIN)
+ // POSIX_REC_INCR_XFER_SIZE :: sysconf(._POSIX_REC_INCR_XFER_SIZE)
+ // POSIX_REC_MAX_XFER_SIZE :: sysconf(._POSIX_REC_MAX_XFER_SIZE)
+ // POSIX_REC_MIN_XFER_SIZE :: sysconf(._POSIX_REC_MIN_XFER_SIZE)
+ // POSIX_REC_XFER_ALIGN :: sysconf(._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 :: 32
+ // LINE_MAX :: sysconf(._LINE_MAX)
+ // NGROUPS_MAX :: sysconf(._NGROUPS_MAX)
+ RE_DUP_MAX :: 255
+
+ // Other limits.
+
+ NL_ARGMAX :: 9
+ NL_LANGMAX :: 32 // 14 on glibc, 32 on musl
+ NL_MSGMAX :: 32_767
+ NL_SETMAX :: 255
+ NL_TEXTMAX :: 2048 // 255 on glibc, 2048 on musl
+ NZERO :: 20
+
}
diff --git a/core/sys/posix/locale.odin b/core/sys/posix/locale.odin
index 1f2a336b5..5b8d7c216 100644
--- a/core/sys/posix/locale.odin
+++ b/core/sys/posix/locale.odin
@@ -1,93 +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 {
- #panic("posix is unimplemented for the current target")
-}
+Locale_Category :: libc.Locale_Category
diff --git a/core/sys/posix/monetary.odin b/core/sys/posix/monetary.odin
index b4f0c31ee..ee342e211 100644
--- a/core/sys/posix/monetary.odin
+++ b/core/sys/posix/monetary.odin
@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
package posix
import "core:c"
diff --git a/core/sys/posix/net_if.odin b/core/sys/posix/net_if.odin
index aaeb5088a..774d11b72 100644
--- a/core/sys/posix/net_if.odin
+++ b/core/sys/posix/net_if.odin
@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
package posix
import "core:c"
@@ -44,7 +45,7 @@ foreign lib {
if_freenameindex :: proc(ptr: ^if_nameindex_t) ---
}
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
// NOTE: `_t` suffix added due to name conflict.
@@ -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")
}
diff --git a/core/sys/posix/netdb.odin b/core/sys/posix/netdb.odin
index 7570f9a22..79e13a140 100644
--- a/core/sys/posix/netdb.odin
+++ b/core/sys/posix/netdb.odin
@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
package posix
import "core:c"
@@ -318,7 +319,7 @@ Info_Errno :: enum c.int {
OVERFLOW = EAI_OVERFLOW,
}
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
hostent :: struct {
h_name: cstring, /* [PSX] official name of host */
@@ -412,9 +413,28 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
NI_NUMERICSERV :: 2
NI_NUMERICSCOPE :: 32
NI_DGRAM :: 16
+
+ } else when ODIN_OS == .Linux {
+
+ AI_PASSIVE :: 0x001
+ AI_CANONNAME :: 0x002
+ AI_NUMERICHOST :: 0x004
+ AI_NUMERICSERV :: 0x400
+ AI_V4MAPPED :: 0x008
+ AI_ALL :: 0x010
+ AI_ADDRCONFIG :: 0x020
+
+ NI_NOFQDN :: 4
+ NI_NUMERICHOST :: 1
+ NI_NAMEREQD :: 8
+ NI_NUMERICSERV :: 2
+ NI_NUMERICSCOPE :: 0x100
+ NI_DGRAM :: 16
+
}
when ODIN_OS == .OpenBSD {
+
EAI_AGAIN :: -3
EAI_BADFLAGS :: -1
EAI_FAIL :: -4
@@ -425,7 +445,22 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
EAI_SOCKTYPE :: -7
EAI_SYSTEM :: -11
EAI_OVERFLOW :: -14
+
+ } else when ODIN_OS == .Linux {
+
+ 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 :: -12
+
} else {
+
EAI_AGAIN :: 2
EAI_BADFLAGS :: 3
EAI_FAIL :: 4
@@ -438,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")
}
diff --git a/core/sys/posix/netinet_in.odin b/core/sys/posix/netinet_in.odin
index 3926c5288..a2cf904ce 100644
--- a/core/sys/posix/netinet_in.odin
+++ b/core/sys/posix/netinet_in.odin
@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
package posix
import "core:c"
@@ -30,7 +31,7 @@ Protocol :: enum c.int {
UDP = IPPROTO_UDP,
}
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
in_addr :: struct {
s_addr: in_addr_t, /* [PSX] big endian address */
@@ -44,26 +45,68 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
},
}
- 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 */
- }
+ when ODIN_OS == .Linux {
+
+ sockaddr_in :: struct {
+ 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_family: sa_family_t, /* [PSX] AF_INET6 (but a smaller size) */
+ sin6_port: in_port_t, /* [PSX] port number */
+ sin6_flowinfo: u32be, /* [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 */
+ }
+
+ IPV6_MULTICAST_IF :: 17
+ IPV6_UNICAST_HOPS :: 16
+ IPV6_MULTICAST_HOPS :: 18
+ IPV6_MULTICAST_LOOP :: 19
+ IPV6_JOIN_GROUP :: 20
+ IPV6_LEAVE_GROUP :: 21
+ IPV6_V6ONLY :: 26
+
+ } else {
+
+ 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 */
+ }
+
+ 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
- ipv6_mreq :: struct {
- ipv6mr_multiaddr: in6_addr, /* [PSX] IPv6 multicast address */
- ipv6mr_interface: c.uint, /* [PSX] interface index */
}
IPPROTO_IP :: 0
@@ -76,14 +119,6 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
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
}
@@ -194,6 +229,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
)
}
-} else {
- #panic("posix is unimplemented for the current target")
}
diff --git a/core/sys/posix/netinet_tcp.odin b/core/sys/posix/netinet_tcp.odin
index ecd084b38..b1da12f5e 100644
--- a/core/sys/posix/netinet_tcp.odin
+++ b/core/sys/posix/netinet_tcp.odin
@@ -1,11 +1,10 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
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 {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
TCP_NODELAY :: 0x01
-} else {
- #panic("posix is unimplemented for the current target")
}
diff --git a/core/sys/posix/poll.odin b/core/sys/posix/poll.odin
index 3e825e009..9c3b8b081 100644
--- a/core/sys/posix/poll.odin
+++ b/core/sys/posix/poll.odin
@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
package posix
import "base:intrinsics"
@@ -72,7 +73,24 @@ 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")
-}
+} else when ODIN_OS == .Linux {
+
+ 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 :: 0x0100
+ POLLWRBAND :: 0x0200
+
+ POLLERR :: 0x0008
+ POLLHUP :: 0x0010
+ POLLNVAL :: 0x0020
+
+}
diff --git a/core/sys/posix/posix.odin b/core/sys/posix/posix.odin
index 5cb4122a6..d56217407 100644
--- a/core/sys/posix/posix.odin
+++ b/core/sys/posix/posix.odin
@@ -1,5 +1,7 @@
/*
-Bindings for most POSIX APIs.
+Raw bindings for most POSIX APIs.
+
+Targets glibc and musl compatibility.
APIs that have been left out are due to not being useful,
being fully replaced (and better) by other Odin packages,
@@ -9,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:
diff --git a/core/sys/posix/pthread.odin b/core/sys/posix/pthread.odin
index e264f6f6c..490064da6 100644
--- a/core/sys/posix/pthread.odin
+++ b/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"
@@ -354,12 +355,16 @@ Thread_Scope :: enum c.int {
}
Cancel_State :: enum c.int {
+ // Cancel takes place at next cancellation point.
ENABLE = PTHREAD_CANCEL_ENABLE,
+ // Cancel postponed.
DISABLE = PTHREAD_CANCEL_DISABLE,
}
Cancel_Type :: enum c.int {
+ // Cancel waits until cancellation point.
DEFERRED = PTHREAD_CANCEL_DEFERRED,
+ // Cancel occurs immediately.
ASYNCHRONOUS = PTHREAD_CANCEL_ASYNCHRONOUS,
}
@@ -371,6 +376,12 @@ when ODIN_OS == .Darwin {
PTHREAD_CANCEL_DISABLE :: 0x00
PTHREAD_CANCEL_ENABLE :: 0x01
+ // PTHREAD_CANCEL_ASYNCHRONOUS :: 1
+ // PTHREAD_CANCEL_DEFERRED :: 0
+ //
+ // PTHREAD_CANCEL_DISABLE :: 1
+ // PTHREAD_CANCEL_ENABLE :: 0
+
PTHREAD_CANCELED :: rawptr(uintptr(1))
PTHREAD_CREATE_DETACHED :: 2
@@ -398,6 +409,16 @@ when ODIN_OS == .Darwin {
pthread_key_t :: distinct c.ulong
+ pthread_mutex_t :: struct {
+ __sig: c.long,
+ __opaque: [56]c.char,
+ }
+
+ pthread_cond_t :: struct {
+ __sig: c.long,
+ __opaque: [40]c.char,
+ }
+
sched_param :: struct {
sched_priority: c.int, /* [PSX] process or thread execution scheduling priority */
_: [4]c.char,
@@ -423,18 +444,28 @@ when ODIN_OS == .Darwin {
PTHREAD_PRIO_NONE :: 0
PTHREAD_PRIO_PROTECT :: 2
- PTHREAD_PROCESS_SHARED :: 0
- PTHREAD_PROCESS_PRIVATE :: 1
+ PTHREAD_PROCESS_SHARED :: 1
+ PTHREAD_PROCESS_PRIVATE :: 0
PTHREAD_SCOPE_PROCESS :: 0
PTHREAD_SCOPE_SYSTEM :: 2
pthread_t :: distinct u64
- pthread_attr_t :: distinct rawptr
+ pthread_attr_t :: struct #align(8) {
+ _: [8]byte,
+ }
pthread_key_t :: distinct c.int
+ pthread_mutex_t :: struct #align(8) {
+ _: [8]byte,
+ }
+
+ pthread_cond_t :: struct #align(8) {
+ _: [8]byte,
+ }
+
sched_param :: struct {
sched_priority: c.int, /* [PSX] process or thread execution scheduling priority */
}
@@ -475,6 +506,14 @@ when ODIN_OS == .Darwin {
pthread_key_t :: distinct c.int
+ pthread_cond_t :: struct #align(8) {
+ _: [40]byte,
+ }
+
+ pthread_mutex_t :: struct #align(8) {
+ _: [48]byte,
+ }
+
sched_param :: struct {
sched_priority: c.int, /* [PSX] process or thread execution scheduling priority */
}
@@ -505,14 +544,68 @@ when ODIN_OS == .Darwin {
PTHREAD_SCOPE_PROCESS :: 0
PTHREAD_SCOPE_SYSTEM :: 0x2
- pthread_t :: distinct rawptr
- pthread_attr_t :: distinct rawptr
- pthread_key_t :: distinct c.int
+ pthread_t :: distinct rawptr
+ pthread_attr_t :: distinct rawptr
+ pthread_key_t :: distinct c.int
+ pthread_mutex_t :: distinct rawptr
+ pthread_cond_t :: distinct rawptr
sched_param :: struct {
sched_priority: c.int, /* [PSX] process or thread execution scheduling priority */
}
-} else {
- #panic("posix is unimplemented for the current target")
+} else when ODIN_OS == .Linux {
+
+ PTHREAD_CANCEL_DEFERRED :: 0
+ PTHREAD_CANCEL_ASYNCHRONOUS :: 1
+
+ PTHREAD_CANCEL_ENABLE :: 0
+ PTHREAD_CANCEL_DISABLE :: 1
+
+ PTHREAD_CANCELED :: rawptr(~uintptr(0))
+
+ PTHREAD_CREATE_JOINABLE :: 0
+ PTHREAD_CREATE_DETACHED :: 1
+
+ PTHREAD_INHERIT_SCHED :: 0
+ PTHREAD_EXPLICIT_SCHED :: 1
+
+ PTHREAD_PRIO_NONE :: 0
+ PTHREAD_PRIO_INHERIT :: 1
+ PTHREAD_PRIO_PROTECT :: 2
+
+ PTHREAD_PROCESS_PRIVATE :: 0
+ PTHREAD_PROCESS_SHARED :: 1
+
+ PTHREAD_SCOPE_SYSTEM :: 0
+ PTHREAD_SCOPE_PROCESS :: 1
+
+ pthread_t :: distinct c.ulong
+
+ pthread_attr_t :: struct #raw_union {
+ __size: [56]c.char, // NOTE: may be smaller depending on libc or arch, but never larger.
+ __align: c.long,
+ }
+
+ pthread_key_t :: distinct c.uint
+
+ pthread_cond_t :: struct {
+ __size: [40]c.char, // NOTE: may be smaller depending on libc or arch, but never larger.
+ __align: c.long,
+ }
+
+ pthread_mutex_t :: struct {
+ __size: [32]c.char, // NOTE: may be smaller depending on libc or arch, but never larger.
+ __align: c.long,
+ }
+
+ sched_param :: struct {
+ sched_priority: c.int, /* [PSX] process or thread execution scheduling priority */
+
+ // NOTE: may be smaller depending on libc or arch, but never larger.
+ __reserved1: c.int,
+ __reserved2: [4]c.long,
+ __reserved3: c.int,
+ }
+
}
diff --git a/core/sys/posix/pwd.odin b/core/sys/posix/pwd.odin
index 546d58309..33cbcd7c5 100644
--- a/core/sys/posix/pwd.odin
+++ b/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. */
+ }
+
}
diff --git a/core/sys/posix/sched.odin b/core/sys/posix/sched.odin
index 6623ba6e6..e91178b09 100644
--- a/core/sys/posix/sched.odin
+++ b/core/sys/posix/sched.odin
@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
package posix
import "core:c"
@@ -94,12 +95,10 @@ when ODIN_OS == .Darwin {
SCHED_RR :: 3
SCHED_OTHER :: 2
-} else when ODIN_OS == .NetBSD {
+} else when ODIN_OS == .NetBSD || ODIN_OS == .Linux {
SCHED_OTHER :: 0
SCHED_FIFO :: 1
SCHED_RR :: 2
-} else {
- #panic("posix is unimplemented for the current target")
}
diff --git a/core/sys/posix/setjmp.odin b/core/sys/posix/setjmp.odin
index cb1dad184..926dbd3ad 100644
--- a/core/sys/posix/setjmp.odin
+++ b/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"
diff --git a/core/sys/posix/setjmp_libc.odin b/core/sys/posix/setjmp_libc.odin
new file mode 100644
index 000000000..a69dff09f
--- /dev/null
+++ b/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
diff --git a/core/sys/posix/signal.odin b/core/sys/posix/signal.odin
index c35494185..4ba4e9943 100644
--- a/core/sys/posix/signal.odin
+++ b/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"
@@ -480,11 +375,6 @@ when ODIN_OS == .Darwin {
uid_t :: distinct c.uint32_t
sigset_t :: distinct c.uint32_t
- // MOTE: unimplemented on darwin.
- //
- // SIGRTMIN ::
- // SIGRTMAX ::
-
SIGHUP :: 1
SIGQUIT :: 3
SIGTRAP :: 5
@@ -625,11 +515,6 @@ when ODIN_OS == .Darwin {
__bits: [4]c.uint32_t,
}
- // MOTE: unimplemented on darwin.
- //
- // SIGRTMIN :: 65
- // SIGRTMAX :: 126
-
SIGHUP :: 1
SIGQUIT :: 3
SIGTRAP :: 5
@@ -794,11 +679,6 @@ when ODIN_OS == .Darwin {
__bits: [4]c.uint32_t,
}
- // MOTE: unimplemented on darwin.
- //
- // SIGRTMIN :: 33
- // SIGRTMAX :: 63
-
SIGHUP :: 1
SIGQUIT :: 3
SIGTRAP :: 5
@@ -1126,6 +1006,178 @@ when ODIN_OS == .Darwin {
SI_ASYNCIO :: -4 // NOTE: not implemented
SI_MESGQ :: -5 // NOTE: not implemented
-} else {
- #panic("posix is unimplemented for the current target")
+} else when ODIN_OS == .Linux {
+
+ // Request that signal be held
+ SIG_HOLD :: rawptr(uintptr(2))
+
+ uid_t :: distinct c.uint32_t
+ sigset_t :: struct {
+ __val: [1024/(8 * size_of(c.ulong))]c.ulong,
+ }
+
+ SIGHUP :: 1
+ SIGQUIT :: 3
+ SIGTRAP :: 5
+ SIGBUS :: 7
+ SIGKILL :: 9
+ SIGUSR1 :: 10
+ SIGUSR2 :: 12
+ SIGPIPE :: 13
+ SIGALRM :: 14
+ SIGCHLD :: 17
+ SIGCONT :: 18
+ SIGSTOP :: 19
+ SIGTSTP :: 20
+ SIGTTIN :: 21
+ SIGTTOU :: 22
+ SIGURG :: 23
+ SIGXCPU :: 24
+ SIGXFSZ :: 25
+ SIGVTALRM :: 26
+ SIGPROF :: 27
+ SIGPOLL :: 29
+ SIGSYS :: 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 */
+ sa_restorer: proc "c" (),
+ }
+
+ SIG_BLOCK :: 0
+ SIG_UNBLOCK :: 1
+ SIG_SETMASK :: 2
+
+ SA_NOCLDSTOP :: 1
+ SA_NOCLDWAIT :: 2
+ SA_SIGINFO :: 4
+ SA_ONSTACK :: 0x08000000
+ SA_RESTART :: 0x10000000
+ SA_NODEFER :: 0x40000000
+ SA_RESETHAND :: 0x80000000
+
+ SS_ONSTACK :: 1
+ SS_DISABLE :: 2
+
+ when ODIN_ARCH == .arm64 {
+ MINSIGSTKSZ :: 6144
+ SIGSTKSZ :: 12288
+ } else {
+ MINSIGSTKSZ :: 2048
+ SIGSTKSZ :: 8192
+ }
+
+ stack_t :: struct {
+ ss_sp: rawptr, /* [PSX] stack base or pointer */
+ ss_flags: SS_Flags, /* [PSX] flags */
+ ss_size: c.size_t, /* [PSX] stack size */
+ }
+
+ @(private)
+ __SI_MAX_SIZE :: 128
+
+ when size_of(int) == 8 {
+ @(private)
+ _pad0 :: struct {
+ _pad0: c.int,
+ }
+ @(private)
+ __SI_PAD_SIZE :: (__SI_MAX_SIZE / size_of(c.int)) - 4
+
+ } else {
+ @(private)
+ _pad0 :: struct {}
+ @(private)
+ __SI_PAD_SIZE :: (__SI_MAX_SIZE / size_of(c.int)) - 3
+ }
+
+ siginfo_t :: struct #align(8) {
+ 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,
+ },
+ __pad0: _pad0,
+ using _sifields: struct #raw_union {
+ _pad: [__SI_PAD_SIZE]c.int,
+
+ using _: struct {
+ si_pid: pid_t, /* [PSX] sending process ID */
+ si_uid: uid_t, /* [PSX] real user ID of sending process */
+ using _: struct #raw_union {
+ si_status: c.int, /* [PSX] exit value or signal */
+ si_value: sigval, /* [PSX] signal value */
+ },
+ },
+ using _: struct {
+ si_addr: rawptr, /* [PSX] address of faulting instruction */
+ },
+ using _: struct {
+ 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 :: -1
+ SI_TIMER :: -2
+ SI_MESGQ :: -3
+ SI_ASYNCIO :: -4
}
diff --git a/core/sys/posix/signal_libc.odin b/core/sys/posix/signal_libc.odin
new file mode 100644
index 000000000..aef22da29
--- /dev/null
+++ b/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
+}
diff --git a/core/sys/posix/stdio.odin b/core/sys/posix/stdio.odin
index de716f8d7..24464dfd8 100644
--- a/core/sys/posix/stdio.odin
+++ b/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"
@@ -33,16 +33,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.
Returns: nil (setting errno) on failure, the stream on success
@@ -116,34 +106,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.
Returns: the number of bytes written or -1 on failure/EOF
@@ -182,60 +144,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.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/renameat.html ]]
@@ -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/"
+
}
diff --git a/core/sys/posix/stdio_libc.odin b/core/sys/posix/stdio_libc.odin
new file mode 100644
index 000000000..fbd949b2c
--- /dev/null
+++ b/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
diff --git a/core/sys/posix/stdlib.odin b/core/sys/posix/stdlib.odin
index a1e2eab50..640c70b5a 100644
--- a/core/sys/posix/stdlib.odin
+++ b/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,
@@ -343,21 +293,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.
Example:
@@ -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"
diff --git a/core/sys/posix/stdlib_libc.odin b/core/sys/posix/stdlib_libc.odin
new file mode 100644
index 000000000..fa4d925b2
--- /dev/null
+++ b/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"
+}
diff --git a/core/sys/posix/string.odin b/core/sys/posix/string.odin
index d22f49a96..96b6a9007 100644
--- a/core/sys/posix/string.odin
+++ b/core/sys/posix/string.odin
@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
package posix
import "core:c"
@@ -14,16 +15,6 @@ when ODIN_OS == .Darwin {
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
@@ -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())
-}
diff --git a/core/sys/posix/string_libc.odin b/core/sys/posix/string_libc.odin
new file mode 100644
index 000000000..336352cbc
--- /dev/null
+++ b/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())
+}
diff --git a/core/sys/posix/sys_ipc.odin b/core/sys/posix/sys_ipc.odin
index f8778ee15..0f7ec06c5 100644
--- a/core/sys/posix/sys_ipc.odin
+++ b/core/sys/posix/sys_ipc.odin
@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
package posix
import "core:c"
@@ -84,6 +85,30 @@ when ODIN_OS == .Darwin {
IPC_SET :: 1
IPC_STAT :: 2
-} else {
- #panic("posix is unimplemented for the current target")
+} else when ODIN_OS == .Linux {
+
+ key_t :: distinct c.int32_t
+
+ ipc_perm :: struct {
+ __ipc_perm_key: key_t,
+ 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 */
+ __ipc_perm_seq: c.int,
+ __pad1: c.long,
+ __pad2: c.long,
+ }
+
+ IPC_CREAT :: 0o01000
+ IPC_EXCL :: 0o02000
+ IPC_NOWAIT :: 0o04000
+
+ IPC_PRIVATE :: key_t(0)
+
+ IPC_RMID :: 0
+ IPC_SET :: 1
+ IPC_STAT :: 2
+
}
diff --git a/core/sys/posix/sys_mman.odin b/core/sys/posix/sys_mman.odin
index 217d321ac..0594672ae 100644
--- a/core/sys/posix/sys_mman.odin
+++ b/core/sys/posix/sys_mman.odin
@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
package posix
import "core:c"
@@ -115,12 +116,14 @@ Prot_Flag_Bits :: enum c.int {
Prot_Flags :: bit_set[Prot_Flag_Bits; c.int]
Map_Flag_Bits :: enum c.int {
+ // Map anonymous memory.
+ ANONYMOUS = log2(MAP_ANONYMOUS),
// Interpret addr exactly.
- FIXED = log2(MAP_FIXED),
+ FIXED = log2(MAP_FIXED),
// Changes are private.
- PRIVATE = log2(MAP_PRIVATE),
+ PRIVATE = log2(MAP_PRIVATE),
// Changes are shared.
- SHARED = log2(MAP_SHARED),
+ SHARED = log2(MAP_SHARED),
}
Map_Flags :: bit_set[Map_Flag_Bits; c.int]
@@ -163,18 +166,19 @@ when ODIN_OS == .NetBSD {
@(private) LMSYNC :: "msync"
}
-when ODIN_OS == .Darwin || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
+when ODIN_OS == .Darwin || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
PROT_EXEC :: 0x04
_PROT_NONE :: 0x00
PROT_READ :: 0x01
PROT_WRITE :: 0x02
- MAP_FIXED :: 0x0010
- MAP_PRIVATE :: 0x0002
- MAP_SHARED :: 0x0001
+ MAP_FIXED :: 0x0010
+ MAP_PRIVATE :: 0x0002
+ MAP_SHARED :: 0x0001
+ MAP_ANONYMOUS :: 0x0020 when ODIN_OS == .Linux else 0x1000
- when ODIN_OS == .Darwin {
+ when ODIN_OS == .Darwin || ODIN_OS == .Linux {
MS_INVALIDATE :: 0x0002
_MS_SYNC :: 0x0010
} else when ODIN_OS == .NetBSD {
@@ -184,6 +188,7 @@ when ODIN_OS == .Darwin || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
MS_INVALIDATE :: 0x0004
_MS_SYNC :: 0x0002
}
+
MS_ASYNC :: 0x0001
MS_SYNC :: Sync_Flags{Sync_Flags_Bits(log2(_MS_SYNC))}
@@ -205,9 +210,10 @@ when ODIN_OS == .Darwin || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
PROT_READ :: 0x01
PROT_WRITE :: 0x02
- MAP_FIXED :: 0x0010
- MAP_PRIVATE :: 0x0002
- MAP_SHARED :: 0x0001
+ MAP_FIXED :: 0x0010
+ MAP_PRIVATE :: 0x0002
+ MAP_SHARED :: 0x0001
+ MAP_ANONYMOUS :: 0x1000
MS_ASYNC :: 0x0001
MS_INVALIDATE :: 0x0002
@@ -224,6 +230,4 @@ when ODIN_OS == .Darwin || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
POSIX_MADV_SEQUENTIAL :: 2
POSIX_MADV_WILLNEED :: 3
-} else {
- #panic("posix is unimplemented for the current target")
}
diff --git a/core/sys/posix/sys_msg.odin b/core/sys/posix/sys_msg.odin
index a8b86e501..0e78777f9 100644
--- a/core/sys/posix/sys_msg.odin
+++ b/core/sys/posix/sys_msg.odin
@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
package posix
import "core:c"
@@ -64,28 +65,22 @@ when ODIN_OS == .Darwin {
MSG_NOERROR :: 0o10000
- // NOTE: this is #pragma pack(4)
-
- msqid_ds :: struct #align(4) {
- msg_perm: ipc_perm, /* [PSX] operation permission structure */
+ msqid_ds :: struct #max_field_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_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,
- },
- },
+ msg_rtime: time_t, /* [PSX] time of last msgrcv() */
+ msg_pad2: c.int32_t,
+ 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 {
@@ -156,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,
+ }
+
}
diff --git a/core/sys/posix/sys_resource.odin b/core/sys/posix/sys_resource.odin
index 6716d60c3..9af2a929b 100644
--- a/core/sys/posix/sys_resource.odin
+++ b/core/sys/posix/sys_resource.odin
@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
package posix
import "core:c"
@@ -95,7 +96,7 @@ when ODIN_OS == .NetBSD {
@(private) LGETRUSAGE :: "getrusage"
}
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
PRIO_PROCESS :: 0
PRIO_PGRP :: 1
@@ -103,7 +104,7 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
rlim_t :: distinct c.uint64_t
- RLIM_INFINITY :: (rlim_t(1) << 63) - 1
+ RLIM_INFINITY :: ~rlim_t(0) when ODIN_OS == .Linux else (rlim_t(1) << 63) - 1
RLIM_SAVED_MAX :: RLIM_INFINITY
RLIM_SAVED_CUR :: RLIM_INFINITY
@@ -143,10 +144,15 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
RLIMIT_CPU :: 0
RLIMIT_DATA :: 2
RLIMIT_FSIZE :: 1
- RLIMIT_NOFILE :: 8
+ RLIMIT_NOFILE :: 7 when ODIN_OS == .Linux else 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")
+ when ODIN_OS == .Linux {
+ RLIMIT_AS :: 9
+ } else when ODIN_OS == .Darwin || ODIN_OS == .OpenBSD {
+ RLIMIT_AS :: 5
+ } else {
+ RLIMIT_AS :: 10
+ }
+
}
diff --git a/core/sys/posix/sys_select.odin b/core/sys/posix/sys_select.odin
index 3392e02bc..2058ee777 100644
--- a/core/sys/posix/sys_select.odin
+++ b/core/sys/posix/sys_select.odin
@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
package posix
import "base:intrinsics"
@@ -55,7 +56,7 @@ when ODIN_OS == .NetBSD {
LSELECT :: "select"
}
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
suseconds_t :: distinct (c.int32_t when ODIN_OS == .Darwin || ODIN_OS == .NetBSD else c.long)
@@ -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")
}
diff --git a/core/sys/posix/sys_sem.odin b/core/sys/posix/sys_sem.odin
index 3fcde325b..6b695e766 100644
--- a/core/sys/posix/sys_sem.odin
+++ b/core/sys/posix/sys_sem.odin
@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
package posix
import "core:c"
@@ -79,19 +80,15 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
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() */
+ semid_ds :: struct #max_field_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,
- },
+ 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 {
@@ -127,6 +124,34 @@ 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")
+} else when ODIN_OS == .Linux {
+
+ SEM_UNDO :: 0x1000 // undo the operation on exit
+
+ // Commands for `semctl'.
+ GETPID :: 11
+ GETVAL :: 12
+ GETALL :: 13
+ GETNCNT :: 14
+ GETZCNT :: 15
+ SETVAL :: 16
+ SETALL :: 17
+
+ semid_ds :: struct {
+ sem_perm: ipc_perm, // [PSX] operation permission structure
+ sem_otime: time_t, // [PSX] last semop()
+ __sem_otime_high: c.ulong,
+ sem_ctime: time_t, // [PSX] last time changed by semctl()
+ __sem_ctime_high: c.ulong,
+ sem_nsems: c.ulong, // [PSX] number of semaphores in set
+ __glibc_reserved3: c.ulong,
+ __glibc_reserved4: c.ulong,
+ }
+
+ sembuf :: struct {
+ sem_num: c.ushort, /* [PSX] semaphore number */
+ sem_op: c.short, /* [PSX] semaphore operation */
+ sem_flg: c.short, /* [PSX] operation flags */
+ }
+
}
diff --git a/core/sys/posix/sys_shm.odin b/core/sys/posix/sys_shm.odin
index 3bc883ce4..8f3c56b9c 100644
--- a/core/sys/posix/sys_shm.odin
+++ b/core/sys/posix/sys_shm.odin
@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
package posix
import "core:c"
@@ -67,20 +68,16 @@ when ODIN_OS == .Darwin {
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,
- },
+ shmid_ds :: struct #max_field_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 */
+ 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 {
@@ -141,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,
+ }
}
diff --git a/core/sys/posix/sys_socket.odin b/core/sys/posix/sys_socket.odin
index 36c3c1467..4dd6074a3 100644
--- a/core/sys/posix/sys_socket.odin
+++ b/core/sys/posix/sys_socket.odin
@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
package posix
import "core:c"
@@ -47,6 +48,12 @@ foreign libc {
addr.sun_family = .UNIX
copy(addr.sun_path[:], "/somepath\x00")
+ /*
+ unlink the socket before binding in case
+ of previous runs not cleaning up the socket
+ */
+ posix.unlink("/somepath")
+
if posix.bind(sfd, (^posix.sockaddr)(&addr), size_of(addr)) != .OK {
/* Handle error */
}
@@ -321,16 +328,25 @@ when ODIN_OS == .NetBSD {
@(private) LSOCKET :: "socket"
}
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
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 */
- sa_data: [14]c.char, /* [PSX] socket address */
+ 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 */
+ }
}
@@ -339,6 +355,11 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
_SS_PAD1SIZE :: 6
@(private)
_SS_PAD2SIZE :: 240
+ } else when ODIN_OS == .Linux {
+ @(private)
+ _SS_SIZE :: 128
+ @(private)
+ _SS_PADSIZE :: _SS_SIZE - size_of(c.uint16_t) - size_of(c.uint64_t)
} else {
@(private)
_SS_MAXSIZE :: 128
@@ -350,28 +371,52 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
_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,
- }
+ when ODIN_OS == .Linux {
+ sockaddr_storage :: struct {
+ ss_family: sa_family_t, /* [PSX] address family */
+ __ss_padding: [_SS_PADSIZE]c.char,
+ __ss_align: c.uint64_t, /* force structure storage alignment */
+ }
- 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 */
- }
+ 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.size_t, /* [PSX] members in msg_iov */
+ msg_control: rawptr, /* [PSX] ancillary data */
+ msg_controllen: c.size_t, /* [PSX] ancillary data buffer length */
+ msg_flags: Msg_Flags, /* [PSX] flags on received message */
+ }
+
+ cmsghdr :: struct {
+ cmsg_len: c.size_t, /* [PSX] data byte count, including cmsghdr */
+ cmsg_level: c.int, /* [PSX] originating protocol */
+ cmsg_type: c.int, /* [PSX] protocol-specific type */
+ }
+ } else {
+ 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,
+ }
- 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 */
+ 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
@@ -421,57 +466,90 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
SOCK_STREAM :: 1
// Options to be accessed at socket level, not protocol level.
- SOL_SOCKET :: 0xffff
-
- SO_ACCEPTCONN :: 0x0002
- SO_BROADCAST :: 0x0020
- SO_DEBUG :: 0x0001
- SO_DONTROUTE :: 0x0010
- SO_ERROR :: 0x1007
- SO_KEEPALIVE :: 0x0008
- SO_OOBINLINE :: 0x0100
- SO_RCVBUF :: 0x1002
- SO_RCVLOWAT :: 0x1004
- SO_REUSEADDR :: 0x0004
- SO_SNDBUF :: 0x1001
- SO_SNDLOWAT :: 0x1003
- SO_TYPE :: 0x1008
-
- when ODIN_OS == .Darwin {
- SO_LINGER :: 0x1080
- SO_RCVTIMEO :: 0x1006
- SO_SNDTIMEO :: 0x1005
- } else when ODIN_OS == .FreeBSD {
- SO_LINGER :: 0x0080
- SO_RCVTIMEO :: 0x1006
- SO_SNDTIMEO :: 0x1005
- } else when ODIN_OS == .NetBSD {
- SO_LINGER :: 0x0080
- SO_RCVTIMEO :: 0x100c
- SO_SNDTIMEO :: 0x100b
- } else when ODIN_OS == .OpenBSD {
- SO_LINGER :: 0x0080
- SO_RCVTIMEO :: 0x1006
- SO_SNDTIMEO :: 0x1005
+ when ODIN_OS == .Linux {
+ SOL_SOCKET :: 1
+
+ SO_ACCEPTCONN :: 30
+ SO_BROADCAST :: 6
+ SO_DEBUG :: 1
+ SO_DONTROUTE :: 5
+ SO_ERROR :: 4
+ SO_KEEPALIVE :: 9
+ SO_OOBINLINE :: 10
+ SO_RCVBUF :: 8
+ SO_RCVLOWAT :: 18
+ SO_REUSEADDR :: 2
+ SO_SNDBUF :: 7
+ SO_SNDLOWAT :: 19
+ SO_TYPE :: 3
+ SO_LINGER :: 13
+
+ SO_RCVTIMEO :: 66
+ SO_SNDTIMEO :: 67
+ } else {
+ SOL_SOCKET :: 0xffff
+
+ SO_ACCEPTCONN :: 0x0002
+ SO_BROADCAST :: 0x0020
+ SO_DEBUG :: 0x0001
+ SO_DONTROUTE :: 0x0010
+ SO_ERROR :: 0x1007
+ SO_KEEPALIVE :: 0x0008
+ SO_OOBINLINE :: 0x0100
+ SO_RCVBUF :: 0x1002
+ SO_RCVLOWAT :: 0x1004
+ SO_REUSEADDR :: 0x0004
+ SO_SNDBUF :: 0x1001
+ SO_SNDLOWAT :: 0x1003
+ SO_TYPE :: 0x1008
+
+ when ODIN_OS == .Darwin {
+ SO_LINGER :: 0x1080
+ SO_RCVTIMEO :: 0x1006
+ SO_SNDTIMEO :: 0x1005
+ } else when ODIN_OS == .FreeBSD {
+ SO_LINGER :: 0x0080
+ SO_RCVTIMEO :: 0x1006
+ SO_SNDTIMEO :: 0x1005
+ } else when ODIN_OS == .NetBSD {
+ SO_LINGER :: 0x0080
+ SO_RCVTIMEO :: 0x100c
+ SO_SNDTIMEO :: 0x100b
+ } else when ODIN_OS == .OpenBSD {
+ SO_LINGER :: 0x0080
+ SO_RCVTIMEO :: 0x1006
+ SO_SNDTIMEO :: 0x1005
+ }
}
// The maximum backlog queue length for listen().
SOMAXCONN :: 128
- MSG_CTRUNC :: 0x20
- MSG_DONTROUTE :: 0x4
- MSG_EOR :: 0x8
- MSG_OOB :: 0x1
- MSG_PEEK :: 0x2
- MSG_TRUNC :: 0x10
- MSG_WAITALL :: 0x40
-
- when ODIN_OS == .Darwin {
- MSG_NOSIGNAL :: 0x80000
- } else when ODIN_OS == .FreeBSD {
- MSG_NOSIGNAL :: 0x00020000
- } else when ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
- MSG_NOSIGNAL :: 0x0400
+ when ODIN_OS == .Linux {
+ MSG_CTRUNC :: 0x008
+ MSG_DONTROUTE :: 0x004
+ MSG_EOR :: 0x080
+ MSG_OOB :: 0x001
+ MSG_PEEK :: 0x002
+ MSG_TRUNC :: 0x020
+ MSG_WAITALL :: 0x100
+ MSG_NOSIGNAL :: 0x4000
+ } else {
+ 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
@@ -483,13 +561,12 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
AF_INET6 :: 28
} else when ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
AF_INET6 :: 24
+ } else when ODIN_OS == .Linux {
+ AF_INET6 :: 10
}
SHUT_RD :: 0
SHUT_RDWR :: 2
SHUT_WR :: 1
-} else {
- #panic("posix is unimplemented for the current target")
}
-
diff --git a/core/sys/posix/sys_stat.odin b/core/sys/posix/sys_stat.odin
index dd66d7d14..61b98ef35 100644
--- a/core/sys/posix/sys_stat.odin
+++ b/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,
+ }
+ }
}
diff --git a/core/sys/posix/sys_statvfs.odin b/core/sys/posix/sys_statvfs.odin
index eb6c16806..47c810135 100644
--- a/core/sys/posix/sys_statvfs.odin
+++ b/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
}
diff --git a/core/sys/posix/sys_time.odin b/core/sys/posix/sys_time.odin
index 093fdd688..3036352aa 100644
--- a/core/sys/posix/sys_time.odin
+++ b/core/sys/posix/sys_time.odin
@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
package posix
import "core:c"
@@ -66,7 +67,7 @@ when ODIN_OS == .NetBSD {
@(private) LUTIMES :: "utimes"
}
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
itimerval :: struct {
it_interval: timeval, /* [PSX] timer interval */
@@ -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")
}
diff --git a/core/sys/posix/sys_times.odin b/core/sys/posix/sys_times.odin
index 685ced515..113e3f963 100644
--- a/core/sys/posix/sys_times.odin
+++ b/core/sys/posix/sys_times.odin
@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
package posix
when ODIN_OS == .Darwin {
@@ -24,7 +25,7 @@ when ODIN_OS == .NetBSD {
@(private) LTIMES :: "times"
}
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
tms :: struct {
tms_utime: clock_t, /* [PSX] user CPU time */
@@ -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")
}
diff --git a/core/sys/posix/sys_uio.odin b/core/sys/posix/sys_uio.odin
index 01664e576..a0ad2934e 100644
--- a/core/sys/posix/sys_uio.odin
+++ b/core/sys/posix/sys_uio.odin
@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
package posix
import "core:c"
@@ -30,13 +31,11 @@ foreign libc {
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 {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
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")
}
diff --git a/core/sys/posix/sys_un.odin b/core/sys/posix/sys_un.odin
index 146882051..ca5c4ee31 100644
--- a/core/sys/posix/sys_un.odin
+++ b/core/sys/posix/sys_un.odin
@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
package posix
import "core:c"
@@ -12,6 +13,11 @@ when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS
sun_path: [104]c.char, /* [PSX] socket pathname */
}
-} else {
- #panic("posix is unimplemented for the current target")
+} else when ODIN_OS == .Linux {
+
+ sockaddr_un :: struct {
+ sun_family: sa_family_t, /* [PSX] address family */
+ sun_path: [108]c.char, /* [PSX] socket pathname */
+ }
+
}
diff --git a/core/sys/posix/sys_utsname.odin b/core/sys/posix/sys_utsname.odin
index 803f40ffd..64930160f 100644
--- a/core/sys/posix/sys_utsname.odin
+++ b/core/sys/posix/sys_utsname.odin
@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
package posix
import "core:c"
@@ -50,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"`,
+ }
}
diff --git a/core/sys/posix/sys_wait.odin b/core/sys/posix/sys_wait.odin
index 8421c8bfd..812bd8c62 100644
--- a/core/sys/posix/sys_wait.odin
+++ b/core/sys/posix/sys_wait.odin
@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
package posix
import "core:c"
@@ -124,11 +125,11 @@ WIFCONTINUED :: #force_inline proc "contextless" (x: c.int) -> bool {
idtype_t :: enum c.int {
// Wait for any children and `id` is ignored.
- P_ALL,
+ P_ALL = _P_ALL,
// Wait for any child wiith a process group ID equal to `id`.
- P_PID,
+ P_PID = _P_PID,
// Wait for any child with a process group ID equal to `id`.
- P_PGID,
+ P_PGID = _P_PGID,
}
Wait_Flag_Bits :: enum c.int {
@@ -166,6 +167,10 @@ when ODIN_OS == .Darwin {
WNOWAIT :: 0x00000020
WSTOPPED :: 0x00000008
+ _P_ALL :: 0
+ _P_PID :: 1
+ _P_PGID :: 2
+
@(private)
_WSTATUS :: #force_inline proc "contextless" (x: c.int) -> c.int {
return x & 0o177
@@ -221,6 +226,10 @@ when ODIN_OS == .Darwin {
WNOWAIT :: 8
WSTOPPED :: 2
+ _P_ALL :: 7
+ _P_PID :: 0
+ _P_PGID :: 2
+
@(private)
_WSTATUS :: #force_inline proc "contextless" (x: c.int) -> c.int {
return x & 0o177
@@ -275,6 +284,10 @@ when ODIN_OS == .Darwin {
WNOWAIT :: 0x00010000
WSTOPPED :: 0x00000002
+ _P_ALL :: 0
+ _P_PID :: 1
+ _P_PGID :: 2
+
@(private)
_WSTATUS :: #force_inline proc "contextless" (x: c.int) -> c.int {
return x & 0o177
@@ -330,6 +343,10 @@ when ODIN_OS == .Darwin {
WNOWAIT :: 0x00010000
WSTOPPED :: 0x00000002
+ _P_ALL :: 0
+ _P_PID :: 2
+ _P_PGID :: 1
+
@(private)
_WSTATUS :: #force_inline proc "contextless" (x: c.int) -> c.int {
return x & 0o177
@@ -375,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
+ }
}
diff --git a/core/sys/posix/termios.odin b/core/sys/posix/termios.odin
index c73936d58..0c07eceb9 100644
--- a/core/sys/posix/termios.odin
+++ b/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")
}
diff --git a/core/sys/posix/time.odin b/core/sys/posix/time.odin
index 5c6ebcf2f..f9c51c63c 100644
--- a/core/sys/posix/time.odin
+++ b/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
+ }
}
diff --git a/core/sys/posix/ulimit.odin b/core/sys/posix/ulimit.odin
index 067b83271..0f87641fa 100644
--- a/core/sys/posix/ulimit.odin
+++ b/core/sys/posix/ulimit.odin
@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
package posix
import "core:c"
@@ -31,13 +32,11 @@ Ulimit_Cmd :: enum c.int {
SETFSIZE = UL_SETFSIZE,
}
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
UL_GETFSIZE :: 1
UL_SETFSIZE :: 2
// NOTE: I don't think OpenBSD implements this API.
-} else {
- #panic("posix is unimplemented for the current target")
}
diff --git a/core/sys/posix/unistd.odin b/core/sys/posix/unistd.odin
index 15dbb576f..0526b3235 100644
--- a/core/sys/posix/unistd.odin
+++ b/core/sys/posix/unistd.odin
@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
package posix
import "core:c"
@@ -12,19 +13,6 @@ when ODIN_OS == .Darwin {
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 ]]
@@ -43,18 +31,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.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchdir.html ]]
@@ -205,15 +181,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.
There shall be no return from a successful exec,
@@ -393,44 +360,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.
Returns: the ID, no failure is defined
@@ -830,13 +759,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.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/setegid.html ]]
@@ -913,13 +835,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.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/sync.html ]]
@@ -959,13 +874,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.
[[ More; https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlink.html ]]
@@ -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,
@@ -1181,20 +1075,20 @@ when ODIN_OS == .Darwin {
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
+ _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
@@ -1362,20 +1256,20 @@ when ODIN_OS == .Darwin {
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
+ _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
@@ -1543,20 +1437,20 @@ when ODIN_OS == .Darwin {
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
+ _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
@@ -1655,7 +1549,6 @@ when ODIN_OS == .Darwin {
_SC_TTY_NAME_MAX :: 68
_SC_HOST_NAME_MAX :: 69
- _SC_PASS_MAX :: 70
_SC_REGEXP :: 71
_SC_SHELL :: 72
_SC_SYMLOOP_MAX :: 73
@@ -1729,20 +1622,20 @@ when ODIN_OS == .Darwin {
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
+ _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
@@ -1911,7 +1804,192 @@ when ODIN_OS == .Darwin {
_POSIX_VDISABLE :: '\377'
-} else {
- #panic("posix is unimplemented for the current target")
-}
+} else when ODIN_OS == .Linux {
+
+ _F_OK :: 0
+ X_OK :: 1
+ W_OK :: 2
+ R_OK :: 4
+
+ F_LOCK :: 1
+ F_TEST :: 3
+ F_TLOCK :: 2
+ F_ULOCK :: 0
+ _CS_PATH :: 1
+ _CS_POSIX_V6_WIDTH_RESTRICTED_ENVS :: 2
+
+ _CS_POSIX_V6_ILP32_OFF32_CFLAGS :: 1116
+ _CS_POSIX_V6_ILP32_OFF32_LDFLAGS :: 1117
+ _CS_POSIX_V6_ILP32_OFF32_LIBS :: 1118
+ _CS_POSIX_V6_ILP32_OFFBIG_CFLAGS :: 1120
+ _CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS :: 1121
+ _CS_POSIX_V6_ILP32_OFFBIG_LIBS :: 1122
+ _CS_POSIX_V6_LP64_OFF64_CFLAGS :: 1124
+ _CS_POSIX_V6_LP64_OFF64_LDFLAGS :: 1125
+ _CS_POSIX_V6_LP64_OFF64_LIBS :: 1126
+ _CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS :: 1128
+ _CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS :: 1129
+ _CS_POSIX_V6_LPBIG_OFFBIG_LIBS :: 1130
+
+ _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_SYNC_IO :: 10
+ _PC_ASYNC_IO :: 11
+ _PC_PRIO_IO :: 12
+ _PC_FILESIZEBITS :: 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_ALLOC_SIZE_MIN :: 19
+ _PC_SYMLINK_MAX :: 20
+ _PC_2_SYMLINK :: 21
+
+ _SC_ARG_MAX :: 1
+ _SC_CHILD_MAX :: 2
+ _SC_CLK_TCK :: 3
+ _SC_NGROUPS_MAX :: 4
+ _SC_OPEN_MAX :: 5
+ _SC_STREAM_MAX :: 6
+ _SC_TZNAME_MAX :: 7
+ _SC_JOB_CONTROL :: 8
+ _SC_SAVED_IDS :: 9
+ _SC_REALTIME_SIGNALS :: 10
+ _SC_PRIORITY_SCHEDULING :: 11
+ _SC_TIMERS :: 12
+ _SC_ASYNCHRONOUS_IO :: 13
+ _SC_PRIORITIZED_IO :: 14
+ _SC_SYNCHRONIZED_IO :: 15
+ _SC_FSYNC :: 16
+ _SC_MAPPED_FILES :: 17
+ _SC_MEMLOCK :: 18
+ _SC_MEMLOCK_RANGE :: 19
+ _SC_MEMORY_PROTECTION :: 20
+ _SC_MESSAGE_PASSING :: 21
+ _SC_SEMAPHORES :: 22
+ _SC_SHARED_MEMORY_OBJECTS :: 23
+ _SC_AIO_LISTIO_MAX :: 24
+ _SC_AIO_MAX :: 25
+ _SC_AIO_PRIO_DELTA_MAX :: 26
+ _SC_DELAYTIMER_MAX :: 27
+ _SC_MQ_OPEN_MAX :: 28
+ _SC_MQ_PRIO_MAX :: 29
+ _SC_VERSION :: 30
+ _SC_PAGESIZE :: 31
+ _SC_PAGE_SIZE :: _SC_PAGESIZE
+ _SC_RTSIG_MAX :: 32
+ _SC_SEM_NSEMS_MAX :: 33
+ _SC_SEM_VALUE_MAX :: 34
+ _SC_SIGQUEUE_MAX :: 35
+ _SC_TIMER_MAX :: 36
+ _SC_BC_BASE_MAX :: 37
+ _SC_BC_DIM_MAX :: 38
+ _SC_BC_SCALE_MAX :: 39
+ _SC_BC_STRING_MAX :: 40
+ _SC_COLL_WEIGHTS_MAX :: 41
+ _SC_EXPR_NEST_MAX :: 43
+ _SC_LINE_MAX :: 44
+ _SC_RE_DUP_MAX :: 45
+ _SC_2_VERSION :: 47
+ _SC_2_C_BIND :: 48
+ _SC_2_C_DEV :: 49
+ _SC_2_FORT_DEV :: 50
+ _SC_2_FORT_RUN :: 51
+ _SC_2_SW_DEV :: 52
+ _SC_2_LOCALEDEF :: 53
+
+ _SC_IOV_MAX :: 62
+ _SC_THREADS :: 69
+ _SC_THREAD_SAFE_FUNCTIONS :: 70
+ _SC_GETGR_R_SIZE_MAX :: 71
+ _SC_GETPW_R_SIZE_MAX :: 72
+ _SC_LOGIN_NAME_MAX :: 73
+ _SC_TTY_NAME_MAX :: 74
+ _SC_THREAD_DESTRUCTOR_ITERATIONS :: 75
+ _SC_THREAD_KEYS_MAX :: 76
+ _SC_THREAD_STACK_MIN :: 77
+ _SC_THREAD_THREADS_MAX :: 78
+ _SC_THREAD_ATTR_STACKADDR :: 79
+ _SC_THREAD_ATTR_STACKSIZE :: 80
+ _SC_THREAD_PRIORITY_SCHEDULING :: 81
+ _SC_THREAD_PRIO_INHERIT :: 82
+ _SC_THREAD_PRIO_PROTECT :: 83
+ _SC_THREAD_PROCESS_SHARED :: 84
+ _SC_NPROCESSORS_CONF :: 85
+ _SC_NPROCESSORS_ONLN :: 86
+ _SC_PHYS_PAGES :: 87
+ _SC_AVPHYS_PAGES :: 88
+ _SC_ATEXIT_MAX :: 89
+ _SC_PASS_MAX :: 90
+ _SC_XOPEN_VERSION :: 91
+ _SC_XOPEN_UNIX :: 92
+ _SC_XOPEN_CRYPT :: 93
+ _SC_XOPEN_ENH_I18N :: 94
+ _SC_XOPEN_SHM :: 95
+ _SC_2_CHAR_TERM :: 96
+ _SC_2_UPE :: 97
+
+ _SC_XOPEN_LEGACY :: 129
+ _SC_XOPEN_REALTIME :: 130
+ _SC_XOPEN_REALTIME_THREADS :: 131
+ _SC_ADVISORY_INFO :: 132
+ _SC_BARRIERS :: 133
+ _SC_CLOCK_SELECTION :: 137
+ _SC_CPUTIME :: 138
+ _SC_THREAD_CPUTIME :: 139
+ _SC_MONOTONIC_CLOCK :: 149
+ _SC_READER_WRITER_LOCKS :: 153
+ _SC_SPIN_LOCKS :: 154
+ _SC_REGEXP :: 155
+ _SC_SHELL :: 157
+ _SC_SPAWN :: 159
+ _SC_SPORADIC_SERVER :: 160
+ _SC_THREAD_SPORADIC_SERVER :: 161
+ _SC_TIMEOUTS :: 164
+ _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
+ _SC_2_PBS_CHECKPOINT :: 174
+ _SC_V6_ILP32_OFF32 :: 175
+ _SC_V6_ILP32_OFFBIG :: 176
+ _SC_V6_LP64_OFF64 :: 177
+ _SC_V6_LPBIG_OFFBIG :: 178
+ _SC_HOST_NAME_MAX :: 179
+ _SC_TRACE :: 180
+ _SC_TRACE_EVENT_FILTER :: 181
+ _SC_TRACE_INHERIT :: 182
+ _SC_TRACE_LOG :: 183
+
+ _SC_IPV6 :: 234
+ _SC_RAW_SOCKETS :: 235
+ _SC_V7_ILP32_OFF32 :: 236
+ _SC_V7_ILP32_OFFBIG :: 237
+ _SC_V7_LP64_OFF64 :: 238
+ _SC_V7_LPBIG_OFFBIG :: 239
+ _SC_SS_REPL_MAX :: 240
+ _SC_TRACE_EVENT_NAME_MAX :: 241
+ _SC_TRACE_NAME_MAX :: 242
+ _SC_TRACE_SYS_MAX :: 243
+ _SC_TRACE_USER_EVENT_MAX :: 244
+ _SC_XOPEN_STREAMS :: 245
+ _SC_THREAD_ROBUST_PRIO_INHERIT :: 246
+ _SC_THREAD_ROBUST_PRIO_PROTECT :: 247
+
+ // NOTE: Not implemented.
+ _SC_XOPEN_UUCP :: 0
+ // NOTE: Not implemented.
+ _POSIX_VDISABLE :: 0
+
+}
diff --git a/core/sys/posix/unistd_libc.odin b/core/sys/posix/unistd_libc.odin
new file mode 100644
index 000000000..bbfe3d59d
--- /dev/null
+++ b/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)
+}
diff --git a/core/sys/posix/utime.odin b/core/sys/posix/utime.odin
index 591a6db06..e884eb1a3 100644
--- a/core/sys/posix/utime.odin
+++ b/core/sys/posix/utime.odin
@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
package posix
when ODIN_OS == .Darwin {
@@ -24,13 +25,11 @@ when ODIN_OS == .NetBSD {
@(private) LUTIME :: "utime"
}
-when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD {
+when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD || ODIN_OS == .NetBSD || ODIN_OS == .OpenBSD || ODIN_OS == .Linux {
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")
}
diff --git a/core/sys/posix/wordexp.odin b/core/sys/posix/wordexp.odin
index d730db0f7..a9e6f39a7 100644
--- a/core/sys/posix/wordexp.odin
+++ b/core/sys/posix/wordexp.odin
@@ -1,3 +1,4 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
package posix
import "core:c"
@@ -102,6 +103,25 @@ when ODIN_OS == .Darwin {
WRDE_NOSPACE :: 4
WRDE_SYNTAX :: 6
-} else {
- #panic("posix is unimplemented for the current target")
+} else when ODIN_OS == .Linux {
+
+ 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_DOOFFS :: 1 << 0 /* Insert PWORDEXP->we_offs NULLs. */
+ WRDE_APPEND :: 1 << 1 /* Append to results of a previous call. */
+ WRDE_NOCMD :: 1 << 2 /* Don't do command substitution. */
+ WRDE_REUSE :: 1 << 3 /* Reuse storage in PWORDEXP. */
+ WRDE_SHOWERR :: 1 << 4 /* Don't redirect stderr to /dev/null. */
+ WRDE_UNDEF :: 1 << 5 /* Error for expanding undefined variables. */
+
+ WRDE_NOSPACE :: 1
+ WRDE_BADCHAR :: 2
+ WRDE_BADVAL :: 3
+ WRDE_CMDSUB :: 4
+ WRDE_SYNTAX :: 5
+
}
diff --git a/core/sys/unix/pthread_darwin.odin b/core/sys/unix/pthread_darwin.odin
deleted file mode 100644
index eb2cc4c9f..000000000
--- a/core/sys/unix/pthread_darwin.odin
+++ /dev/null
@@ -1,96 +0,0 @@
-#+build darwin
-package unix
-
-import "core:c"
-
-// NOTE(tetra): No 32-bit Macs.
-// Source: _pthread_types.h on my Mac.
-PTHREAD_SIZE :: 8176
-PTHREAD_ATTR_SIZE :: 56
-PTHREAD_MUTEXATTR_SIZE :: 8
-PTHREAD_MUTEX_SIZE :: 56
-PTHREAD_CONDATTR_SIZE :: 8
-PTHREAD_COND_SIZE :: 40
-PTHREAD_ONCE_SIZE :: 8
-PTHREAD_RWLOCK_SIZE :: 192
-PTHREAD_RWLOCKATTR_SIZE :: 16
-
-pthread_t :: distinct u64
-
-pthread_attr_t :: struct {
- sig: c.long,
- _: [PTHREAD_ATTR_SIZE] c.char,
-}
-
-pthread_cond_t :: struct {
- sig: c.long,
- _: [PTHREAD_COND_SIZE] c.char,
-}
-
-pthread_condattr_t :: struct {
- sig: c.long,
- _: [PTHREAD_CONDATTR_SIZE] c.char,
-}
-
-pthread_mutex_t :: struct {
- sig: c.long,
- _: [PTHREAD_MUTEX_SIZE] c.char,
-}
-
-pthread_mutexattr_t :: struct {
- sig: c.long,
- _: [PTHREAD_MUTEXATTR_SIZE] c.char,
-}
-
-pthread_once_t :: struct {
- sig: c.long,
- _: [PTHREAD_ONCE_SIZE] c.char,
-}
-
-pthread_rwlock_t :: struct {
- sig: c.long,
- _: [PTHREAD_RWLOCK_SIZE] c.char,
-}
-
-pthread_rwlockattr_t :: struct {
- sig: c.long,
- _: [PTHREAD_RWLOCKATTR_SIZE] c.char,
-}
-
-SCHED_OTHER :: 1 // Avoid if you are writing portable software.
-SCHED_FIFO :: 4
-SCHED_RR :: 2 // Round robin.
-
-SCHED_PARAM_SIZE :: 4
-
-sched_param :: struct {
- sched_priority: c.int,
- _: [SCHED_PARAM_SIZE] c.char,
-}
-
-// Source: https://github.com/apple/darwin-libpthread/blob/03c4628c8940cca6fd6a82957f683af804f62e7f/pthread/pthread.h#L138
-PTHREAD_CREATE_JOINABLE :: 1
-PTHREAD_CREATE_DETACHED :: 2
-PTHREAD_INHERIT_SCHED :: 1
-PTHREAD_EXPLICIT_SCHED :: 2
-PTHREAD_PROCESS_SHARED :: 1
-PTHREAD_PROCESS_PRIVATE :: 2
-
-
-PTHREAD_MUTEX_NORMAL :: 0
-PTHREAD_MUTEX_RECURSIVE :: 1
-PTHREAD_MUTEX_ERRORCHECK :: 2
-
-PTHREAD_CANCEL_ENABLE :: 0
-PTHREAD_CANCEL_DISABLE :: 1
-PTHREAD_CANCEL_DEFERRED :: 0
-PTHREAD_CANCEL_ASYNCHRONOUS :: 1
-
-foreign import pthread "system:System.framework"
-
-@(default_calling_convention="c")
-foreign pthread {
- pthread_setcancelstate :: proc (state: c.int, old_state: ^c.int) -> c.int ---
- pthread_setcanceltype :: proc (type: c.int, old_type: ^c.int) -> c.int ---
- pthread_cancel :: proc (thread: pthread_t) -> c.int ---
-}
diff --git a/core/sys/unix/pthread_freebsd.odin b/core/sys/unix/pthread_freebsd.odin
deleted file mode 100644
index 38fe7db55..000000000
--- a/core/sys/unix/pthread_freebsd.odin
+++ /dev/null
@@ -1,122 +0,0 @@
-#+build freebsd
-package unix
-
-import "core:c"
-
-pthread_t :: distinct u64
-// pthread_t :: struct #align(16) { x: u64 }
-
-PTHREAD_COND_T_SIZE :: 8
-
-PTHREAD_MUTEXATTR_T_SIZE :: 8
-PTHREAD_CONDATTR_T_SIZE :: 8
-PTHREAD_RWLOCKATTR_T_SIZE :: 8
-PTHREAD_BARRIERATTR_T_SIZE :: 8
-
-// WARNING: The sizes of these things are different yet again
-// on non-X86!
-when size_of(int) == 8 {
- PTHREAD_ATTR_T_SIZE :: 8
- PTHREAD_MUTEX_T_SIZE :: 8
- PTHREAD_RWLOCK_T_SIZE :: 8
- PTHREAD_BARRIER_T_SIZE :: 8
-} else when size_of(int) == 4 { // TODO
- PTHREAD_ATTR_T_SIZE :: 32
- PTHREAD_MUTEX_T_SIZE :: 32
- PTHREAD_RWLOCK_T_SIZE :: 44
- PTHREAD_BARRIER_T_SIZE :: 20
-}
-
-pthread_cond_t :: struct #align(16) {
- _: [PTHREAD_COND_T_SIZE] c.char,
-}
-pthread_mutex_t :: struct #align(16) {
- _: [PTHREAD_MUTEX_T_SIZE] c.char,
-}
-pthread_rwlock_t :: struct #align(16) {
- _: [PTHREAD_RWLOCK_T_SIZE] c.char,
-}
-pthread_barrier_t :: struct #align(16) {
- _: [PTHREAD_BARRIER_T_SIZE] c.char,
-}
-
-pthread_attr_t :: struct #align(16) {
- _: [PTHREAD_ATTR_T_SIZE] c.char,
-}
-pthread_condattr_t :: struct #align(16) {
- _: [PTHREAD_CONDATTR_T_SIZE] c.char,
-}
-pthread_mutexattr_t :: struct #align(16) {
- _: [PTHREAD_MUTEXATTR_T_SIZE] c.char,
-}
-pthread_rwlockattr_t :: struct #align(16) {
- _: [PTHREAD_RWLOCKATTR_T_SIZE] c.char,
-}
-pthread_barrierattr_t :: struct #align(16) {
- _: [PTHREAD_BARRIERATTR_T_SIZE] c.char,
-}
-
-PTHREAD_MUTEX_ERRORCHECK :: 1
-PTHREAD_MUTEX_RECURSIVE :: 2
-PTHREAD_MUTEX_NORMAL :: 3
-
-
-PTHREAD_CREATE_JOINABLE :: 0
-PTHREAD_CREATE_DETACHED :: 1
-PTHREAD_INHERIT_SCHED :: 4
-PTHREAD_EXPLICIT_SCHED :: 0
-PTHREAD_PROCESS_PRIVATE :: 0
-PTHREAD_PROCESS_SHARED :: 1
-
-SCHED_FIFO :: 1
-SCHED_OTHER :: 2
-SCHED_RR :: 3 // Round robin.
-
-
-sched_param :: struct {
- sched_priority: c.int,
-}
-
-_usem :: struct {
- _has_waiters: u32,
- _count: u32,
- _flags: u32,
-}
-_usem2 :: struct {
- _count: u32,
- _flags: u32,
-}
-sem_t :: struct {
- _magic: u32,
- _kern: _usem2,
- _padding: u32,
-}
-
-PTHREAD_CANCEL_ENABLE :: 0
-PTHREAD_CANCEL_DISABLE :: 1
-PTHREAD_CANCEL_DEFERRED :: 0
-PTHREAD_CANCEL_ASYNCHRONOUS :: 2
-
-foreign import "system:pthread"
-
-@(default_calling_convention="c")
-foreign pthread {
- // create named semaphore.
- // used in process-shared semaphores.
- sem_open :: proc(name: cstring, flags: c.int) -> ^sem_t ---
-
- sem_init :: proc(sem: ^sem_t, pshared: c.int, initial_value: c.uint) -> c.int ---
- sem_destroy :: proc(sem: ^sem_t) -> c.int ---
- sem_post :: proc(sem: ^sem_t) -> c.int ---
- sem_wait :: proc(sem: ^sem_t) -> c.int ---
- sem_trywait :: proc(sem: ^sem_t) -> c.int ---
- // sem_timedwait :: proc(sem: ^sem_t, timeout: time.TimeSpec) -> c.int ---
-
- // NOTE: unclear whether pthread_yield is well-supported on Linux systems,
- // see https://linux.die.net/man/3/pthread_yield
- pthread_yield :: proc() ---
-
- pthread_setcancelstate :: proc (state: c.int, old_state: ^c.int) -> c.int ---
- pthread_setcanceltype :: proc (type: c.int, old_type: ^c.int) -> c.int ---
- pthread_cancel :: proc (thread: pthread_t) -> c.int ---
-}
diff --git a/core/sys/unix/pthread_haiku.odin b/core/sys/unix/pthread_haiku.odin
deleted file mode 100644
index 1278f34fe..000000000
--- a/core/sys/unix/pthread_haiku.odin
+++ /dev/null
@@ -1,71 +0,0 @@
-package unix
-
-import "core:c"
-
-pthread_t :: distinct rawptr
-pthread_attr_t :: distinct rawptr
-pthread_mutex_t :: distinct rawptr
-pthread_mutexattr_t :: distinct rawptr
-pthread_cond_t :: distinct rawptr
-pthread_condattr_t :: distinct rawptr
-pthread_rwlock_t :: distinct rawptr
-pthread_rwlockattr_t :: distinct rawptr
-pthread_barrier_t :: distinct rawptr
-pthread_barrierattr_t :: distinct rawptr
-pthread_spinlock_t :: distinct rawptr
-
-pthread_key_t :: distinct c.int
-pthread_once_t :: struct {
- state: c.int,
- mutex: pthread_mutex_t,
-}
-
-PTHREAD_MUTEX_DEFAULT :: 0
-PTHREAD_MUTEX_NORMAL :: 1
-PTHREAD_MUTEX_ERRORCHECK :: 2
-PTHREAD_MUTEX_RECURSIVE :: 3
-
-PTHREAD_DETACHED :: 0x1
-PTHREAD_SCOPE_SYSTEM :: 0x2
-PTHREAD_INHERIT_SCHED :: 0x4
-PTHREAD_NOFLOAT :: 0x8
-
-PTHREAD_CREATE_DETACHED :: PTHREAD_DETACHED
-PTHREAD_CREATE_JOINABLE :: 0
-PTHREAD_SCOPE_PROCESS :: 0
-PTHREAD_EXPLICIT_SCHED :: 0
-
-SCHED_FIFO :: 1
-SCHED_RR :: 2
-SCHED_SPORADIC :: 3
-SCHED_OTHER :: 4
-
-sched_param :: struct {
- sched_priority: c.int,
-}
-
-sem_t :: distinct rawptr
-
-PTHREAD_CANCEL_ENABLE :: 0
-PTHREAD_CANCEL_DISABLE :: 1
-PTHREAD_CANCEL_DEFERRED :: 0
-PTHREAD_CANCEL_ASYNCHRONOUS :: 2
-
-foreign import libc "system:c"
-
-@(default_calling_convention="c")
-foreign libc {
- sem_open :: proc(name: cstring, flags: c.int) -> ^sem_t ---
-
- sem_init :: proc(sem: ^sem_t, pshared: c.int, initial_value: c.uint) -> c.int ---
- sem_destroy :: proc(sem: ^sem_t) -> c.int ---
- sem_post :: proc(sem: ^sem_t) -> c.int ---
- sem_wait :: proc(sem: ^sem_t) -> c.int ---
- sem_trywait :: proc(sem: ^sem_t) -> c.int ---
-
- pthread_yield :: proc() ---
-
- pthread_setcancelstate :: proc (state: c.int, old_state: ^c.int) -> c.int ---
- pthread_setcanceltype :: proc (type: c.int, old_type: ^c.int) -> c.int ---
- pthread_cancel :: proc (thread: pthread_t) -> c.int ---
-}
diff --git a/core/sys/unix/pthread_linux.odin b/core/sys/unix/pthread_linux.odin
deleted file mode 100644
index d67add24b..000000000
--- a/core/sys/unix/pthread_linux.odin
+++ /dev/null
@@ -1,124 +0,0 @@
-#+build linux
-package unix
-
-import "core:c"
-
-// TODO(tetra): For robustness, I'd like to mark this with align 16.
-// I cannot currently do this.
-// And at the time of writing there is a bug with putting it
-// as the only field in a struct.
-pthread_t :: distinct u64
-// pthread_t :: struct #align(16) { x: u64 };
-
-// NOTE(tetra): Got all the size constants from pthreadtypes-arch.h on my
-// Linux machine.
-
-PTHREAD_COND_T_SIZE :: 48
-
-PTHREAD_MUTEXATTR_T_SIZE :: 4
-PTHREAD_CONDATTR_T_SIZE :: 4
-PTHREAD_RWLOCKATTR_T_SIZE :: 8
-PTHREAD_BARRIERATTR_T_SIZE :: 4
-
-// WARNING: The sizes of these things are different yet again
-// on non-X86!
-when size_of(int) == 8 {
- PTHREAD_ATTR_T_SIZE :: 56
- PTHREAD_MUTEX_T_SIZE :: 40
- PTHREAD_RWLOCK_T_SIZE :: 56
- PTHREAD_BARRIER_T_SIZE :: 32
-} else when size_of(int) == 4 {
- PTHREAD_ATTR_T_SIZE :: 32
- PTHREAD_MUTEX_T_SIZE :: 32
- PTHREAD_RWLOCK_T_SIZE :: 44
- PTHREAD_BARRIER_T_SIZE :: 20
-}
-
-pthread_cond_t :: struct #align(16) {
- _: [PTHREAD_COND_T_SIZE] c.char,
-}
-pthread_mutex_t :: struct #align(16) {
- _: [PTHREAD_MUTEX_T_SIZE] c.char,
-}
-pthread_rwlock_t :: struct #align(16) {
- _: [PTHREAD_RWLOCK_T_SIZE] c.char,
-}
-pthread_barrier_t :: struct #align(16) {
- _: [PTHREAD_BARRIER_T_SIZE] c.char,
-}
-
-pthread_attr_t :: struct #align(16) {
- _: [PTHREAD_ATTR_T_SIZE] c.char,
-}
-pthread_condattr_t :: struct #align(16) {
- _: [PTHREAD_CONDATTR_T_SIZE] c.char,
-}
-pthread_mutexattr_t :: struct #align(16) {
- _: [PTHREAD_MUTEXATTR_T_SIZE] c.char,
-}
-pthread_rwlockattr_t :: struct #align(16) {
- _: [PTHREAD_RWLOCKATTR_T_SIZE] c.char,
-}
-pthread_barrierattr_t :: struct #align(16) {
- _: [PTHREAD_BARRIERATTR_T_SIZE] c.char,
-}
-
-PTHREAD_MUTEX_NORMAL :: 0
-PTHREAD_MUTEX_RECURSIVE :: 1
-PTHREAD_MUTEX_ERRORCHECK :: 2
-
-
-// TODO(tetra, 2019-11-01): Maybe make `enum c.int`s for these?
-PTHREAD_CREATE_JOINABLE :: 0
-PTHREAD_CREATE_DETACHED :: 1
-PTHREAD_INHERIT_SCHED :: 0
-PTHREAD_EXPLICIT_SCHED :: 1
-PTHREAD_PROCESS_PRIVATE :: 0
-PTHREAD_PROCESS_SHARED :: 1
-
-SCHED_OTHER :: 0
-SCHED_FIFO :: 1
-SCHED_RR :: 2 // Round robin.
-
-sched_param :: struct {
- sched_priority: c.int,
-}
-
-sem_t :: struct #align(16) {
- _: [SEM_T_SIZE] c.char,
-}
-
-when size_of(int) == 8 {
- SEM_T_SIZE :: 32
-} else when size_of(int) == 4 {
- SEM_T_SIZE :: 16
-}
-
-PTHREAD_CANCEL_ENABLE :: 0
-PTHREAD_CANCEL_DISABLE :: 1
-PTHREAD_CANCEL_DEFERRED :: 0
-PTHREAD_CANCEL_ASYNCHRONOUS :: 1
-
-foreign import "system:pthread"
-
-@(default_calling_convention="c")
-foreign pthread {
- // create named semaphore.
- // used in process-shared semaphores.
- sem_open :: proc(name: cstring, flags: c.int) -> ^sem_t ---
-
- sem_init :: proc(sem: ^sem_t, pshared: c.int, initial_value: c.uint) -> c.int ---
- sem_destroy :: proc(sem: ^sem_t) -> c.int ---
- sem_post :: proc(sem: ^sem_t) -> c.int ---
- sem_wait :: proc(sem: ^sem_t) -> c.int ---
- sem_trywait :: proc(sem: ^sem_t) -> c.int ---
- // sem_timedwait :: proc(sem: ^sem_t, timeout: time.TimeSpec) -> c.int ---;
-
- // NOTE: unclear whether pthread_yield is well-supported on Linux systems,
- // see https://linux.die.net/man/3/pthread_yield
- pthread_yield :: proc() -> c.int ---
-
- pthread_setcancelstate :: proc (state: c.int, old_state: ^c.int) -> c.int ---
- pthread_setcanceltype :: proc (type: c.int, old_type: ^c.int) -> c.int ---
- pthread_cancel :: proc (thread: pthread_t) -> c.int ---
-}
diff --git a/core/sys/unix/pthread_netbsd.odin b/core/sys/unix/pthread_netbsd.odin
deleted file mode 100644
index 9107f1139..000000000
--- a/core/sys/unix/pthread_netbsd.odin
+++ /dev/null
@@ -1,102 +0,0 @@
-package unix
-
-import "core:c"
-
-pthread_t :: distinct rawptr
-
-SEM_T_SIZE :: 8
-
-PTHREAD_CONDATTR_T_SIZE :: 16
-PTHREAD_MUTEXATTR_T_SIZE :: 16
-PTHREAD_RWLOCKATTR_T_SIZE :: 16
-PTHREAD_BARRIERATTR_T_SIZE :: 16
-
-PTHREAD_COND_T_SIZE :: 40
-PTHREAD_MUTEX_T_SIZE :: 48
-PTHREAD_RWLOCK_T_SIZE :: 64
-PTHREAD_BARRIER_T_SIZE :: 48
-PTHREAD_ATTR_T_SIZE :: 16
-
-pthread_cond_t :: struct #align(8) {
- _: [PTHREAD_COND_T_SIZE] c.char,
-}
-
-pthread_mutex_t :: struct #align(8) {
- _: [PTHREAD_MUTEX_T_SIZE] c.char,
-}
-
-pthread_rwlock_t :: struct #align(8) {
- _: [PTHREAD_RWLOCK_T_SIZE] c.char,
-}
-
-pthread_barrier_t :: struct #align(8) {
- _: [PTHREAD_BARRIER_T_SIZE] c.char,
-}
-
-pthread_attr_t :: struct #align(8) {
- _: [PTHREAD_ATTR_T_SIZE] c.char,
-}
-
-pthread_condattr_t :: struct #align(8) {
- _: [PTHREAD_CONDATTR_T_SIZE] c.char,
-}
-
-pthread_mutexattr_t :: struct #align(8) {
- _: [PTHREAD_MUTEXATTR_T_SIZE] c.char,
-}
-
-pthread_rwlockattr_t :: struct #align(8) {
- _: [PTHREAD_RWLOCKATTR_T_SIZE] c.char,
-}
-
-pthread_barrierattr_t :: struct #align(8) {
- _: [PTHREAD_BARRIERATTR_T_SIZE] c.char,
-}
-
-PTHREAD_MUTEX_NORMAL :: 0
-PTHREAD_MUTEX_ERRORCHECK :: 1
-PTHREAD_MUTEX_RECURSIVE :: 2
-
-PTHREAD_CREATE_JOINABLE :: 0
-PTHREAD_CREATE_DETACHED :: 1
-PTHREAD_INHERIT_SCHED :: 0
-PTHREAD_EXPLICIT_SCHED :: 1
-PTHREAD_PROCESS_PRIVATE :: 0
-PTHREAD_PROCESS_SHARED :: 1
-
-SCHED_NONE :: -1
-SCHED_OTHER :: 0
-SCHED_FIFO :: 1
-SCHED_RR :: 3
-
-sched_param :: struct {
- sched_priority: c.int,
-}
-
-sem_t :: struct #align(16) {
- _: [SEM_T_SIZE] c.char,
-}
-
-PTHREAD_CANCEL_ENABLE :: 0
-PTHREAD_CANCEL_DISABLE :: 1
-PTHREAD_CANCEL_DEFERRED :: 0
-PTHREAD_CANCEL_ASYNCHRONOUS :: 1
-
-foreign import "system:pthread"
-
-@(default_calling_convention="c")
-foreign pthread {
- sem_open :: proc(name: cstring, flags: c.int) -> ^sem_t ---
-
- sem_init :: proc(sem: ^sem_t, pshared: c.int, initial_value: c.uint) -> c.int ---
- sem_destroy :: proc(sem: ^sem_t) -> c.int ---
- sem_post :: proc(sem: ^sem_t) -> c.int ---
- sem_wait :: proc(sem: ^sem_t) -> c.int ---
- sem_trywait :: proc(sem: ^sem_t) -> c.int ---
-
- pthread_yield :: proc() ---
-
- pthread_setcancelstate :: proc (state: c.int, old_state: ^c.int) -> c.int ---
- pthread_setcanceltype :: proc (type: c.int, old_type: ^c.int) -> c.int ---
- pthread_cancel :: proc (thread: pthread_t) -> c.int ---
-}
diff --git a/core/sys/unix/pthread_openbsd.odin b/core/sys/unix/pthread_openbsd.odin
deleted file mode 100644
index 2c6d9e598..000000000
--- a/core/sys/unix/pthread_openbsd.odin
+++ /dev/null
@@ -1,74 +0,0 @@
-#+build openbsd
-package unix
-
-import "core:c"
-
-pthread_t :: distinct rawptr
-pthread_attr_t :: distinct rawptr
-pthread_mutex_t :: distinct rawptr
-pthread_mutexattr_t :: distinct rawptr
-pthread_cond_t :: distinct rawptr
-pthread_condattr_t :: distinct rawptr
-pthread_rwlock_t :: distinct rawptr
-pthread_rwlockattr_t :: distinct rawptr
-pthread_barrier_t :: distinct rawptr
-pthread_barrierattr_t :: distinct rawptr
-pthread_spinlock_t :: distinct rawptr
-
-pthread_key_t :: distinct c.int
-pthread_once_t :: struct {
- state: c.int,
- mutex: pthread_mutex_t,
-}
-
-PTHREAD_MUTEX_ERRORCHECK :: 1
-PTHREAD_MUTEX_RECURSIVE :: 2
-PTHREAD_MUTEX_NORMAL :: 3
-PTHREAD_MUTEX_STRICT_NP :: 4
-
-PTHREAD_DETACHED :: 0x1
-PTHREAD_SCOPE_SYSTEM :: 0x2
-PTHREAD_INHERIT_SCHED :: 0x4
-PTHREAD_NOFLOAT :: 0x8
-
-PTHREAD_CREATE_DETACHED :: PTHREAD_DETACHED
-PTHREAD_CREATE_JOINABLE :: 0
-PTHREAD_SCOPE_PROCESS :: 0
-PTHREAD_EXPLICIT_SCHED :: 0
-
-SCHED_FIFO :: 1
-SCHED_OTHER :: 2
-SCHED_RR :: 3
-
-sched_param :: struct {
- sched_priority: c.int,
-}
-
-sem_t :: distinct rawptr
-
-PTHREAD_CANCEL_ENABLE :: 0
-PTHREAD_CANCEL_DISABLE :: 1
-PTHREAD_CANCEL_DEFERRED :: 0
-PTHREAD_CANCEL_ASYNCHRONOUS :: 2
-
-foreign import libc "system:c"
-
-@(default_calling_convention="c")
-foreign libc {
- sem_open :: proc(name: cstring, flags: c.int) -> ^sem_t ---
-
- sem_init :: proc(sem: ^sem_t, pshared: c.int, initial_value: c.uint) -> c.int ---
- sem_destroy :: proc(sem: ^sem_t) -> c.int ---
- sem_post :: proc(sem: ^sem_t) -> c.int ---
- sem_wait :: proc(sem: ^sem_t) -> c.int ---
- sem_trywait :: proc(sem: ^sem_t) -> c.int ---
- //sem_timedwait :: proc(sem: ^sem_t, timeout: time.TimeSpec) -> c.int ---
-
- // NOTE: unclear whether pthread_yield is well-supported on Linux systems,
- // see https://linux.die.net/man/3/pthread_yield
- pthread_yield :: proc() ---
-
- pthread_setcancelstate :: proc (state: c.int, old_state: ^c.int) -> c.int ---
- pthread_setcanceltype :: proc (type: c.int, old_type: ^c.int) -> c.int ---
- pthread_cancel :: proc (thread: pthread_t) -> c.int ---
-}
diff --git a/core/sys/unix/pthread_unix.odin b/core/sys/unix/pthread_unix.odin
deleted file mode 100644
index 43c4866ed..000000000
--- a/core/sys/unix/pthread_unix.odin
+++ /dev/null
@@ -1,127 +0,0 @@
-#+build linux, darwin, freebsd, openbsd, netbsd, haiku
-package unix
-
-foreign import "system:pthread"
-
-import "core:c"
-
-timespec :: struct {
- tv_sec: i64,
- tv_nsec: i64,
-}
-
-//
-// On success, these functions return 0.
-//
-
-@(default_calling_convention="c")
-foreign pthread {
- pthread_create :: proc(t: ^pthread_t, attrs: ^pthread_attr_t, routine: proc(data: rawptr) -> rawptr, arg: rawptr) -> c.int ---
-
- // retval is a pointer to a location to put the return value of the thread proc.
- pthread_join :: proc(t: pthread_t, retval: ^rawptr) -> c.int ---
-
- pthread_kill :: proc(t: pthread_t, sig: c.int) -> c.int ---
-
- pthread_self :: proc() -> pthread_t ---
-
- pthread_equal :: proc(a, b: pthread_t) -> b32 ---
-
- pthread_detach :: proc(t: pthread_t) -> c.int ---
-
- sched_get_priority_min :: proc(policy: c.int) -> c.int ---
- sched_get_priority_max :: proc(policy: c.int) -> c.int ---
-
- // NOTE: POSIX says this can fail with OOM.
- pthread_attr_init :: proc(attrs: ^pthread_attr_t) -> c.int ---
-
- pthread_attr_destroy :: proc(attrs: ^pthread_attr_t) -> c.int ---
-
- pthread_attr_getschedparam :: proc(attrs: ^pthread_attr_t, param: ^sched_param) -> c.int ---
- pthread_attr_setschedparam :: proc(attrs: ^pthread_attr_t, param: ^sched_param) -> c.int ---
-
- // states: PTHREAD_CREATE_DETACHED, PTHREAD_CREATE_JOINABLE
- pthread_attr_setdetachstate :: proc(attrs: ^pthread_attr_t, detach_state: c.int) -> c.int ---
-
- // NOTE(tetra, 2019-11-06): WARNING: Different systems have different alignment requirements.
- // For maximum usefulness, use the OS's page size.
- // ALSO VERY MAJOR WARNING: `stack_ptr` must be the LAST byte of the stack on systems
- // where the stack grows downwards, which is the common case, so far as I know.
- // On systems where it grows upwards, give the FIRST byte instead.
- // ALSO SLIGHTLY LESS MAJOR WARNING: Using this procedure DISABLES automatically-provided
- // guard pages. If you are using this procedure, YOU must set them up manually.
- // If you forget to do this, you WILL get stack corruption bugs if you do not EXTREMELY
- // know what you are doing!
- pthread_attr_setstack :: proc(attrs: ^pthread_attr_t, stack_ptr: rawptr, stack_size: u64) -> c.int ---
- pthread_attr_getstack :: proc(attrs: ^pthread_attr_t, stack_ptr: ^rawptr, stack_size: ^u64) -> c.int ---
-
- pthread_sigmask :: proc(how: c.int, set: rawptr, oldset: rawptr) -> c.int ---
-
- sched_yield :: proc() -> c.int ---
-}
-
-// NOTE: Unimplemented in Haiku.
-when ODIN_OS != .Haiku {
- foreign pthread {
- // scheds: PTHREAD_INHERIT_SCHED, PTHREAD_EXPLICIT_SCHED
- pthread_attr_setinheritsched :: proc(attrs: ^pthread_attr_t, sched: c.int) -> c.int ---
-
- pthread_attr_getschedpolicy :: proc(t: ^pthread_attr_t, policy: ^c.int) -> c.int ---
- pthread_attr_setschedpolicy :: proc(t: ^pthread_attr_t, policy: c.int) -> c.int ---
- }
-}
-
-@(default_calling_convention="c")
-foreign pthread {
- // NOTE: POSIX says this can fail with OOM.
- pthread_cond_init :: proc(cond: ^pthread_cond_t, attrs: ^pthread_condattr_t) -> c.int ---
-
- pthread_cond_destroy :: proc(cond: ^pthread_cond_t) -> c.int ---
-
- pthread_cond_signal :: proc(cond: ^pthread_cond_t) -> c.int ---
-
- // same as signal, but wakes up _all_ threads that are waiting
- pthread_cond_broadcast :: proc(cond: ^pthread_cond_t) -> c.int ---
-
-
- // assumes the mutex is pre-locked
- pthread_cond_wait :: proc(cond: ^pthread_cond_t, mutex: ^pthread_mutex_t) -> c.int ---
- pthread_cond_timedwait :: proc(cond: ^pthread_cond_t, mutex: ^pthread_mutex_t, timeout: ^timespec) -> c.int ---
-
- pthread_condattr_init :: proc(attrs: ^pthread_condattr_t) -> c.int ---
- pthread_condattr_destroy :: proc(attrs: ^pthread_condattr_t) -> c.int ---
-
- // p-shared = "process-shared" - i.e: is this condition shared among multiple processes?
- // values: PTHREAD_PROCESS_PRIVATE, PTHREAD_PROCESS_SHARED
- pthread_condattr_setpshared :: proc(attrs: ^pthread_condattr_t, value: c.int) -> c.int ---
- pthread_condattr_getpshared :: proc(attrs: ^pthread_condattr_t, result: ^c.int) -> c.int ---
-
-}
-
-@(default_calling_convention="c")
-foreign pthread {
- // NOTE: POSIX says this can fail with OOM.
- pthread_mutex_init :: proc(mutex: ^pthread_mutex_t, attrs: ^pthread_mutexattr_t) -> c.int ---
-
- pthread_mutex_destroy :: proc(mutex: ^pthread_mutex_t) -> c.int ---
-
- pthread_mutex_trylock :: proc(mutex: ^pthread_mutex_t) -> c.int ---
-
- pthread_mutex_lock :: proc(mutex: ^pthread_mutex_t) -> c.int ---
-
- pthread_mutex_timedlock :: proc(mutex: ^pthread_mutex_t, timeout: ^timespec) -> c.int ---
-
- pthread_mutex_unlock :: proc(mutex: ^pthread_mutex_t) -> c.int ---
-
-
- pthread_mutexattr_init :: proc(attrs: ^pthread_mutexattr_t) -> c.int ---
- pthread_mutexattr_destroy :: proc(attrs: ^pthread_mutexattr_t) -> c.int ---
- pthread_mutexattr_settype :: proc(attrs: ^pthread_mutexattr_t, type: c.int) -> c.int ---
-
- // p-shared = "process-shared" - i.e: is this mutex shared among multiple processes?
- // values: PTHREAD_PROCESS_PRIVATE, PTHREAD_PROCESS_SHARED
- pthread_mutexattr_setpshared :: proc(attrs: ^pthread_mutexattr_t, value: c.int) -> c.int ---
- pthread_mutexattr_getpshared :: proc(attrs: ^pthread_mutexattr_t, result: ^c.int) -> c.int ---
-
- pthread_testcancel :: proc () ---
-}
diff --git a/core/sys/unix/unix.odin b/core/sys/unix/unix.odin
new file mode 100644
index 000000000..e9f58e554
--- /dev/null
+++ b/core/sys/unix/unix.odin
@@ -0,0 +1,8 @@
+package unix
+
+import "core:c"
+
+timespec :: struct {
+ secs: i64,
+ nsecs: c.long,
+}
diff --git a/core/sys/wasm/js/dom.odin b/core/sys/wasm/js/dom.odin
index ffc58a9a3..902dfc941 100644
--- a/core/sys/wasm/js/dom.odin
+++ b/core/sys/wasm/js/dom.odin
@@ -20,6 +20,8 @@ foreign dom_lib {
device_pixel_ratio :: proc() -> f64 ---
window_set_scroll :: proc(x, y: f64) ---
+
+ set_element_style :: proc(id: string, key: string, value: string) ---
}
get_element_value_string :: proc "contextless" (id: string, buf: []byte) -> string {
diff --git a/core/sys/wasm/js/events.odin b/core/sys/wasm/js/events.odin
index 905b3eba9..ffa3a1202 100644
--- a/core/sys/wasm/js/events.odin
+++ b/core/sys/wasm/js/events.odin
@@ -186,8 +186,8 @@ Key_Location :: enum u8 {
Numpad = 3,
}
-KEYBOARD_MAX_KEY_SIZE :: 16
-KEYBOARD_MAX_CODE_SIZE :: 16
+KEYBOARD_MAX_KEY_SIZE :: 32
+KEYBOARD_MAX_CODE_SIZE :: 32
GAMEPAD_MAX_ID_SIZE :: 64
GAMEPAD_MAX_MAPPING_SIZE :: 64
diff --git a/core/sys/wasm/js/odin.js b/core/sys/wasm/js/odin.js
index bf002da74..07a77952c 100644
--- a/core/sys/wasm/js/odin.js
+++ b/core/sys/wasm/js/odin.js
@@ -1259,13 +1259,26 @@ class WebGLInterface {
};
-function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory, eventQueue, event_temp) {
+function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory) {
const MAX_INFO_CONSOLE_LINES = 512;
let infoConsoleLines = new Array();
let currentLine = {};
currentLine[false] = "";
currentLine[true] = "";
let prevIsError = false;
+
+ let event_temp = {};
+
+ const onEventReceived = (event_data, data, callback) => {
+ event_temp.data = event_data;
+
+ const exports = wasmMemoryInterface.exports;
+ const odin_ctx = exports.default_context_ptr();
+
+ exports.odin_dom_do_event_callback(data, callback, odin_ctx);
+
+ event_temp.data = null;
+ };
const writeToConsole = (line, isError) => {
if (!line) {
@@ -1535,8 +1548,8 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory, ev
wmi.storeInt(off(W, W), e.key.length)
wmi.storeInt(off(W, W), e.code.length)
- wmi.storeString(off(16, 1), e.key);
- wmi.storeString(off(16, 1), e.code);
+ wmi.storeString(off(32, 1), e.key);
+ wmi.storeString(off(32, 1), e.code);
} else if (e.type === 'scroll') {
wmi.storeF64(off(8, 8), window.scrollX);
wmi.storeF64(off(8, 8), window.scrollY);
@@ -1594,7 +1607,7 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory, ev
event_data.event = e;
event_data.name_code = name_code;
- eventQueue.push({event_data: event_data, data: data, callback: callback});
+ onEventReceived(event_data, data, callback);
};
wasmMemoryInterface.listenerMap[{data: data, callback: callback}] = listener;
element.addEventListener(name, listener, !!use_capture);
@@ -1611,7 +1624,7 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory, ev
event_data.event = e;
event_data.name_code = name_code;
- eventQueue.push({event_data: event_data, data: data, callback: callback});
+ onEventReceived(event_data, data, callback);
};
wasmMemoryInterface.listenerMap[{data: data, callback: callback}] = listener;
element.addEventListener(name, listener, !!use_capture);
@@ -1802,6 +1815,16 @@ function odinSetupDefaultImports(wasmMemoryInterface, consoleElement, memory, ev
}
},
+ set_element_style: (id_ptr, id_len, key_ptr, key_len, value_ptr, value_len) => {
+ let id = wasmMemoryInterface.loadString(id_ptr, id_len);
+ let key = wasmMemoryInterface.loadString(key_ptr, key_len);
+ let value = wasmMemoryInterface.loadString(value_ptr, value_len);
+ let element = getElement(id);
+ if (element) {
+ element.style[key] = value;
+ }
+ },
+
get_element_key_f64: (id_ptr, id_len, key_ptr, key_len) => {
let id = wasmMemoryInterface.loadString(id_ptr, id_len);
let key = wasmMemoryInterface.loadString(key_ptr, key_len);
@@ -1905,10 +1928,7 @@ async function runWasm(wasmPath, consoleElement, extraForeignImports, wasmMemory
}
wasmMemoryInterface.setIntSize(intSize);
- let eventQueue = new Array();
- let event_temp = {};
-
- let imports = odinSetupDefaultImports(wasmMemoryInterface, consoleElement, wasmMemoryInterface.memory, eventQueue, event_temp);
+ let imports = odinSetupDefaultImports(wasmMemoryInterface, consoleElement, wasmMemoryInterface.memory);
let exports = {};
if (extraForeignImports !== undefined) {
@@ -1933,7 +1953,7 @@ async function runWasm(wasmPath, consoleElement, extraForeignImports, wasmMemory
exports._start();
- // Define a `@export step :: proc(current_type: f64) -> (keep_going: bool) {`
+ // Define a `@export step :: proc(delta_time: f64) -> (keep_going: bool) {`
// in your app and it will get called every frame.
// return `false` to stop the execution of the module.
if (exports.step) {
@@ -1948,14 +1968,7 @@ async function runWasm(wasmPath, consoleElement, extraForeignImports, wasmMemory
const dt = (currTimeStamp - prevTimeStamp)*0.001;
prevTimeStamp = currTimeStamp;
- while (eventQueue.length > 0) {
- let e = eventQueue.shift()
- event_temp.data = e.event_data;
- exports.odin_dom_do_event_callback(e.data, e.callback, odin_ctx);
- }
- event_temp.data = null;
-
- if (!exports.step(currTimeStamp*0.001, odin_ctx)) {
+ if (!exports.step(dt, odin_ctx)) {
exports._end();
return;
}
diff --git a/core/sys/windows/icu.odin b/core/sys/windows/icu.odin
new file mode 100644
index 000000000..6ed8c9b40
--- /dev/null
+++ b/core/sys/windows/icu.odin
@@ -0,0 +1,14 @@
+#+build windows
+package sys_windows
+
+foreign import "system:icu.lib"
+
+UError :: enum i32 {
+ U_ZERO_ERROR = 0,
+}
+
+@(default_calling_convention="system")
+foreign icu {
+ ucal_getWindowsTimeZoneID :: proc(id: wstring, len: i32, winid: wstring, winidCapacity: i32, status: ^UError) -> i32 ---
+ ucal_getDefaultTimeZone :: proc(result: wstring, cap: i32, status: ^UError) -> i32 ---
+}
diff --git a/core/sys/windows/kernel32.odin b/core/sys/windows/kernel32.odin
index 2771581e6..8be50bceb 100644
--- a/core/sys/windows/kernel32.odin
+++ b/core/sys/windows/kernel32.odin
@@ -381,6 +381,14 @@ foreign kernel32 {
nDefaultTimeOut: DWORD,
lpSecurityAttributes: LPSECURITY_ATTRIBUTES,
) -> HANDLE ---
+ PeekNamedPipe :: proc(
+ hNamedPipe: HANDLE,
+ lpBuffer: rawptr,
+ nBufferSize: u32,
+ lpBytesRead: ^u32,
+ lpTotalBytesAvail: ^u32,
+ lpBytesLeftThisMessage: ^u32,
+ ) -> BOOL ---
CancelIo :: proc(handle: HANDLE) -> BOOL ---
GetOverlappedResult :: proc(
hFile: HANDLE,
diff --git a/core/sys/windows/types.odin b/core/sys/windows/types.odin
index 934d103e5..e1ace4133 100644
--- a/core/sys/windows/types.odin
+++ b/core/sys/windows/types.odin
@@ -253,8 +253,6 @@ FILE_GENERIC_WRITE: DWORD : STANDARD_RIGHTS_WRITE |
FILE_APPEND_DATA |
SYNCHRONIZE
-FILE_FLAG_OPEN_REPARSE_POINT: DWORD : 0x00200000
-FILE_FLAG_BACKUP_SEMANTICS: DWORD : 0x02000000
SECURITY_SQOS_PRESENT: DWORD : 0x00100000
FIONBIO: c_ulong : 0x8004667e
@@ -2222,11 +2220,22 @@ WAIT_OBJECT_0: DWORD : 0x00000000
WAIT_TIMEOUT: DWORD : 258
WAIT_FAILED: DWORD : 0xFFFFFFFF
+FILE_FLAG_WRITE_THROUGH: DWORD : 0x80000000
+FILE_FLAG_OVERLAPPED: DWORD : 0x40000000
+FILE_FLAG_NO_BUFFERING: DWORD : 0x20000000
+FILE_FLAG_RANDOM_ACCESS: DWORD : 0x10000000
+FILE_FLAG_SEQUENTIAL_SCAN: DWORD : 0x08000000
+FILE_FLAG_DELETE_ON_CLOSE: DWORD : 0x04000000
+FILE_FLAG_BACKUP_SEMANTICS: DWORD : 0x02000000
+FILE_FLAG_POSIX_SEMANTICS: DWORD : 0x01000000
+FILE_FLAG_SESSION_AWARE: DWORD : 0x00800000
+FILE_FLAG_OPEN_REPARSE_POINT: DWORD : 0x00200000
+FILE_FLAG_OPEN_NO_RECALL: DWORD : 0x00100000
+FILE_FLAG_FIRST_PIPE_INSTANCE: DWORD : 0x00080000
+
PIPE_ACCESS_INBOUND: DWORD : 0x00000001
PIPE_ACCESS_OUTBOUND: DWORD : 0x00000002
PIPE_ACCESS_DUPLEX: DWORD : 0x00000003
-FILE_FLAG_FIRST_PIPE_INSTANCE: DWORD : 0x00080000
-FILE_FLAG_OVERLAPPED: DWORD : 0x40000000
PIPE_WAIT: DWORD : 0x00000000
PIPE_TYPE_BYTE: DWORD : 0x00000000
PIPE_TYPE_MESSAGE: DWORD : 0x00000004
diff --git a/core/sys/windows/user32.odin b/core/sys/windows/user32.odin
index 514592e7b..4ae33cd32 100644
--- a/core/sys/windows/user32.odin
+++ b/core/sys/windows/user32.odin
@@ -781,3 +781,64 @@ CF_GDIOBJLAST :: 0x03FF
CF_OWNERDISPLAY :: 0x0080
CF_PRIVATEFIRST :: 0x0200
CF_PRIVATELAST :: 0x02FF
+
+STICKYKEYS :: struct {
+ cbSize: UINT,
+ dwFlags: DWORD,
+}
+LPSTICKYKEYS :: ^STICKYKEYS
+
+SKF_STICKYKEYSON :: 0x1
+SKF_AVAILABLE :: 0x2
+SKF_HOTKEYACTIVE :: 0x4
+SKF_CONFIRMHOTKEY :: 0x8
+SKF_HOTKEYSOUND :: 0x10
+SKF_INDICATOR :: 0x20
+SKF_AUDIBLEFEEDBACK :: 0x40
+SKF_TRISTATE :: 0x80
+SKF_TWOKEYSOFF :: 0x100
+SKF_LSHIFTLOCKED :: 0x10000
+SKF_RSHIFTLOCKED :: 0x20000
+SKF_LCTLLOCKED :: 0x40000
+SKF_RCTLLOCKED :: 0x80000
+SKF_LALTLOCKED :: 0x100000
+SKF_RALTLOCKED :: 0x200000
+SKF_LWINLOCKED :: 0x400000
+SKF_RWINLOCKED :: 0x800000
+SKF_LSHIFTLATCHED :: 0x1000000
+SKF_RSHIFTLATCHED :: 0x2000000
+SKF_LCTLLATCHED :: 0x4000000
+SKF_RCTLLATCHED :: 0x8000000
+SKF_LALTLATCHED :: 0x10000000
+SKF_RALTLATCHED :: 0x20000000
+
+TOGGLEKEYS :: struct {
+ cbSize: UINT,
+ dwFlags: DWORD,
+}
+LPTOGGLEKEYS :: ^TOGGLEKEYS
+
+TKF_TOGGLEKEYSON :: 0x1
+TKF_AVAILABLE :: 0x2
+TKF_HOTKEYACTIVE :: 0x4
+TKF_CONFIRMHOTKEY :: 0x8
+TKF_HOTKEYSOUND :: 0x10
+TKF_INDICATOR :: 0x20
+
+FILTERKEYS :: struct {
+ cbSize: UINT,
+ dwFlags: DWORD,
+ iWaitMSec: DWORD,
+ iDelayMSec: DWORD,
+ iRepeatMSec: DWORD,
+ iBounceMSec: DWORD,
+}
+LPFILTERKEYS :: ^FILTERKEYS
+
+FKF_FILTERKEYSON :: 0x1
+FKF_AVAILABLE :: 0x2
+FKF_HOTKEYACTIVE :: 0x4
+FKF_CONFIRMHOTKEY :: 0x8
+FKF_HOTKEYSOUND :: 0x10
+FKF_INDICATOR :: 0x20
+FKF_CLICKON :: 0x40
diff --git a/core/sys/windows/ux_theme.odin b/core/sys/windows/ux_theme.odin
index 527abd62f..392cf1e18 100644
--- a/core/sys/windows/ux_theme.odin
+++ b/core/sys/windows/ux_theme.odin
@@ -3,7 +3,7 @@ package sys_windows
foreign import uxtheme "system:UxTheme.lib"
-MARGINS :: distinct [4]int
+MARGINS :: distinct [4]i32
PMARGINS :: ^MARGINS
@(default_calling_convention="system")
diff --git a/core/sys/windows/winerror.odin b/core/sys/windows/winerror.odin
index b3b470619..61a7d9d86 100644
--- a/core/sys/windows/winerror.odin
+++ b/core/sys/windows/winerror.odin
@@ -251,26 +251,26 @@ SEVERITY :: enum DWORD {
// Generic test for success on any status value (non-negative numbers indicate success).
SUCCEEDED :: #force_inline proc "contextless" (#any_int result: int) -> bool { return result >= S_OK }
// and the inverse
-FAILED :: #force_inline proc(#any_int result: int) -> bool { return result < S_OK }
+FAILED :: #force_inline proc "contextless" (#any_int result: int) -> bool { return result < S_OK }
// Generic test for error on any status value.
-IS_ERROR :: #force_inline proc(#any_int status: int) -> bool { return u32(status) >> 31 == u32(SEVERITY.ERROR) }
+IS_ERROR :: #force_inline proc "contextless" (#any_int status: int) -> bool { return u32(status) >> 31 == u32(SEVERITY.ERROR) }
// Return the code
-HRESULT_CODE :: #force_inline proc(#any_int hr: int) -> int { return int(u32(hr) & 0xFFFF) }
+HRESULT_CODE :: #force_inline proc "contextless" (#any_int hr: int) -> int { return int(u32(hr) & 0xFFFF) }
// Return the facility
-HRESULT_FACILITY :: #force_inline proc(#any_int hr: int) -> FACILITY { return FACILITY((u32(hr) >> 16) & 0x1FFF) }
+HRESULT_FACILITY :: #force_inline proc "contextless" (#any_int hr: int) -> FACILITY { return FACILITY((u32(hr) >> 16) & 0x1FFF) }
// Return the severity
-HRESULT_SEVERITY :: #force_inline proc(#any_int hr: int) -> SEVERITY { return SEVERITY((u32(hr) >> 31) & 0x1) }
+HRESULT_SEVERITY :: #force_inline proc "contextless" (#any_int hr: int) -> SEVERITY { return SEVERITY((u32(hr) >> 31) & 0x1) }
// Create an HRESULT value from component pieces
-MAKE_HRESULT :: #force_inline proc(#any_int sev: int, #any_int fac: int, #any_int code: int) -> HRESULT {
+MAKE_HRESULT :: #force_inline proc "contextless" (#any_int sev: int, #any_int fac: int, #any_int code: int) -> HRESULT {
return HRESULT((uint(sev)<<31) | (uint(fac)<<16) | (uint(code)))
}
-DECODE_HRESULT :: #force_inline proc(#any_int hr: int) -> (SEVERITY, FACILITY, int) {
+DECODE_HRESULT :: #force_inline proc "contextless" (#any_int hr: int) -> (SEVERITY, FACILITY, int) {
return HRESULT_SEVERITY(hr), HRESULT_FACILITY(hr), HRESULT_CODE(hr)
}
diff --git a/core/testing/signal_handler_libc.odin b/core/testing/signal_handler_libc.odin
index 7442c100c..281fbde40 100644
--- a/core/testing/signal_handler_libc.odin
+++ b/core/testing/signal_handler_libc.odin
@@ -15,7 +15,6 @@ import "core:c/libc"
import "core:encoding/ansi"
import "core:sync"
import "core:os"
-@require import "core:sys/unix"
@(private="file") stop_runner_flag: libc.sig_atomic_t
@@ -45,7 +44,7 @@ stop_runner_callback :: proc "c" (sig: libc.int) {
}
}
-@(private="file")
+@(private)
stop_test_callback :: proc "c" (sig: libc.int) {
if !local_test_index_set {
// We're a thread created by a test thread.
@@ -106,17 +105,7 @@ This is a dire bug and should be reported to the Odin developers.
// otherwise we may continue to generate signals.
intrinsics.cpu_relax()
- when ODIN_OS != .Windows {
- // NOTE(Feoramund): Some UNIX-like platforms may require this.
- //
- // During testing, I found that NetBSD 10.0 refused to
- // terminate a task thread, even when its thread had been
- // properly set to PTHREAD_CANCEL_ASYNCHRONOUS.
- //
- // The runner would stall after returning from `pthread_cancel`.
-
- unix.pthread_testcancel()
- }
+ _test_thread_cancel()
}
}
}
@@ -133,14 +122,12 @@ _setup_signal_handler :: proc() {
// For tests:
// Catch asserts and panics.
libc.signal(libc.SIGILL, stop_test_callback)
- when ODIN_OS == .Linux || ODIN_OS == .FreeBSD || ODIN_OS == .Haiku || ODIN_OS == .OpenBSD || ODIN_OS == .NetBSD || ODIN_OS == .Darwin {
- // Catch panics on Darwin and unhandled calls to `debug_trap`.
- libc.signal(SIGTRAP, stop_test_callback)
- }
// Catch arithmetic errors.
libc.signal(libc.SIGFPE, stop_test_callback)
// Catch segmentation faults (illegal memory access).
libc.signal(libc.SIGSEGV, stop_test_callback)
+
+ __setup_signal_handler()
}
_setup_task_signal_handler :: proc(test_index: int) {
diff --git a/core/testing/signal_handler_posix.odin b/core/testing/signal_handler_posix.odin
new file mode 100644
index 000000000..1bfcc875b
--- /dev/null
+++ b/core/testing/signal_handler_posix.odin
@@ -0,0 +1,22 @@
+#+build linux, darwin, netbsd, openbsd, freebsd
+#+private
+package testing
+
+import "core:c/libc"
+import "core:sys/posix"
+
+__setup_signal_handler :: proc() {
+ libc.signal(posix.SIGTRAP, stop_test_callback)
+}
+
+_test_thread_cancel :: proc "contextless" () {
+ // NOTE(Feoramund): Some UNIX-like platforms may require this.
+ //
+ // During testing, I found that NetBSD 10.0 refused to
+ // terminate a task thread, even when its thread had been
+ // properly set to PTHREAD_CANCEL_ASYNCHRONOUS.
+ //
+ // The runner would stall after returning from `pthread_cancel`.
+
+ posix.pthread_testcancel()
+}
diff --git a/core/testing/signal_handler_windows.odin b/core/testing/signal_handler_windows.odin
new file mode 100644
index 000000000..74ebe2998
--- /dev/null
+++ b/core/testing/signal_handler_windows.odin
@@ -0,0 +1,6 @@
+#+private
+package testing
+
+__setup_signal_handler :: proc() {}
+
+_test_thread_cancel :: proc "contextless" () {}
diff --git a/core/text/scanner/scanner.odin b/core/text/scanner/scanner.odin
index d27c66f24..24dbcc8a4 100644
--- a/core/text/scanner/scanner.odin
+++ b/core/text/scanner/scanner.odin
@@ -2,7 +2,7 @@
// It takes a string providing the source, which then can be tokenized through
// repeated calls to the scan procedure.
// For compatibility with existing tooling and languages, the NUL character is not allowed.
-// If an UTF-8 encoded byte order mark (BOM) is the first character in the first character in the source, it will be discarded.
+// If an UTF-8 encoded byte order mark (BOM) is the first character in the source, it will be discarded.
//
// By default, a Scanner skips white space and Odin comments and recognizes all literals defined by the Odin programming language specification.
// A Scanner may be customized to recognize only a subset of those literals and to recognize different identifiers and white space characters.
diff --git a/core/thread/thread_unix.odin b/core/thread/thread_unix.odin
index 9576a3040..ff79cfcbc 100644
--- a/core/thread/thread_unix.odin
+++ b/core/thread/thread_unix.odin
@@ -4,14 +4,14 @@ package thread
import "base:runtime"
import "core:sync"
-import "core:sys/unix"
+import "core:sys/posix"
_IS_SUPPORTED :: true
// NOTE(tetra): Aligned here because of core/unix/pthread_linux.odin/pthread_t.
// Also see core/sys/darwin/mach_darwin.odin/semaphore_t.
Thread_Os_Specific :: struct #align(16) {
- unix_thread: unix.pthread_t, // NOTE: very large on Darwin, small on Linux.
+ unix_thread: posix.pthread_t, // NOTE: very large on Darwin, small on Linux.
start_ok: sync.Sema,
}
//
@@ -23,7 +23,9 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
t := (^Thread)(t)
// We need to give the thread a moment to start up before we enable cancellation.
- can_set_thread_cancel_state := unix.pthread_setcancelstate(unix.PTHREAD_CANCEL_ENABLE, nil) == 0
+ // NOTE(laytan): setting to .DISABLE on darwin, with .ENABLE pthread_cancel would deadlock
+ // most of the time, don't ask me why.
+ can_set_thread_cancel_state := posix.pthread_setcancelstate(.DISABLE when ODIN_OS == .Darwin else .ENABLE, nil) == nil
t.id = sync.current_thread_id()
@@ -36,9 +38,15 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
}
// Enable thread's cancelability.
- if can_set_thread_cancel_state {
- unix.pthread_setcanceltype (unix.PTHREAD_CANCEL_ASYNCHRONOUS, nil)
- unix.pthread_setcancelstate(unix.PTHREAD_CANCEL_ENABLE, nil)
+ // NOTE(laytan): Darwin does not correctly/fully support all of this, not doing this does
+ // actually make pthread_cancel work in the capacity of my tests, while executing this would
+ // basically always make it deadlock.
+ if ODIN_OS != .Darwin && can_set_thread_cancel_state {
+ err := posix.pthread_setcancelstate(.ENABLE, nil)
+ assert_contextless(err == nil)
+
+ err = posix.pthread_setcanceltype(.ASYNCHRONOUS, nil)
+ assert_contextless(err == nil)
}
{
@@ -59,8 +67,8 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
sync.atomic_or(&t.flags, { .Done })
if .Self_Cleanup in sync.atomic_load(&t.flags) {
- res := unix.pthread_detach(t.unix_thread)
- assert_contextless(res == 0)
+ res := posix.pthread_detach(t.unix_thread)
+ assert_contextless(res == nil)
t.unix_thread = {}
// NOTE(ftphikari): It doesn't matter which context 'free' received, right?
@@ -71,19 +79,19 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
return nil
}
- attrs: unix.pthread_attr_t
- if unix.pthread_attr_init(&attrs) != 0 {
+ attrs: posix.pthread_attr_t
+ if posix.pthread_attr_init(&attrs) != nil {
return nil // NOTE(tetra, 2019-11-01): POSIX OOM.
}
- defer unix.pthread_attr_destroy(&attrs)
+ defer posix.pthread_attr_destroy(&attrs)
// NOTE(tetra, 2019-11-01): These only fail if their argument is invalid.
- res: i32
- res = unix.pthread_attr_setdetachstate(&attrs, unix.PTHREAD_CREATE_JOINABLE)
- assert(res == 0)
+ res: posix.Errno
+ res = posix.pthread_attr_setdetachstate(&attrs, .CREATE_JOINABLE)
+ assert(res == nil)
when ODIN_OS != .Haiku && ODIN_OS != .NetBSD {
- res = unix.pthread_attr_setinheritsched(&attrs, unix.PTHREAD_EXPLICIT_SCHED)
- assert(res == 0)
+ res = posix.pthread_attr_setinheritsched(&attrs, .EXPLICIT_SCHED)
+ assert(res == nil)
}
thread := new(Thread)
@@ -93,26 +101,26 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
thread.creation_allocator = context.allocator
// Set thread priority.
- policy: i32
+ policy: posix.Sched_Policy
when ODIN_OS != .Haiku && ODIN_OS != .NetBSD {
- res = unix.pthread_attr_getschedpolicy(&attrs, &policy)
- assert(res == 0)
+ res = posix.pthread_attr_getschedpolicy(&attrs, &policy)
+ assert(res == nil)
}
- params: unix.sched_param
- res = unix.pthread_attr_getschedparam(&attrs, &params)
- assert(res == 0)
- low := unix.sched_get_priority_min(policy)
- high := unix.sched_get_priority_max(policy)
+ params: posix.sched_param
+ res = posix.pthread_attr_getschedparam(&attrs, &params)
+ assert(res == nil)
+ low := posix.sched_get_priority_min(policy)
+ high := posix.sched_get_priority_max(policy)
switch priority {
case .Normal: // Okay
case .Low: params.sched_priority = low + 1
case .High: params.sched_priority = high
}
- res = unix.pthread_attr_setschedparam(&attrs, &params)
- assert(res == 0)
+ res = posix.pthread_attr_setschedparam(&attrs, &params)
+ assert(res == nil)
thread.procedure = procedure
- if unix.pthread_create(&thread.unix_thread, &attrs, __unix_thread_entry_proc, thread) != 0 {
+ if posix.pthread_create(&thread.unix_thread, &attrs, __unix_thread_entry_proc, thread) != nil {
free(thread, thread.creation_allocator)
return nil
}
@@ -130,7 +138,7 @@ _is_done :: proc(t: ^Thread) -> bool {
}
_join :: proc(t: ^Thread) {
- if unix.pthread_equal(unix.pthread_self(), t.unix_thread) {
+ if posix.pthread_equal(posix.pthread_self(), t.unix_thread) {
return
}
@@ -144,7 +152,7 @@ _join :: proc(t: ^Thread) {
if .Started not_in sync.atomic_load(&t.flags) {
_start(t)
}
- unix.pthread_join(t.unix_thread, nil)
+ posix.pthread_join(t.unix_thread, nil)
}
_join_multiple :: proc(threads: ..^Thread) {
@@ -170,9 +178,9 @@ _terminate :: proc(t: ^Thread, exit_code: int) {
//
// This is in contrast to behavior I have seen on Linux where the thread is
// just terminated.
- unix.pthread_cancel(t.unix_thread)
+ posix.pthread_cancel(t.unix_thread)
}
_yield :: proc() {
- unix.sched_yield()
+ posix.sched_yield()
}
diff --git a/core/time/datetime/constants.odin b/core/time/datetime/constants.odin
index 5f336ef4a..e24709e49 100644
--- a/core/time/datetime/constants.odin
+++ b/core/time/datetime/constants.odin
@@ -77,12 +77,55 @@ Time :: struct {
nano: i32,
}
+TZ_Record :: struct {
+ time: i64,
+ utc_offset: i64,
+ shortname: string,
+ dst: bool,
+}
+
+TZ_Date_Kind :: enum {
+ No_Leap,
+ Leap,
+ Month_Week_Day,
+}
+
+TZ_Transition_Date :: struct {
+ type: TZ_Date_Kind,
+
+ month: u8,
+ week: u8,
+ day: u16,
+
+ time: i64,
+}
+
+TZ_RRule :: struct {
+ has_dst: bool,
+
+ std_name: string,
+ std_offset: i64,
+ std_date: TZ_Transition_Date,
+
+ dst_name: string,
+ dst_offset: i64,
+ dst_date: TZ_Transition_Date,
+}
+
+TZ_Region :: struct {
+ name: string,
+ records: []TZ_Record,
+ shortnames: []string,
+ rrule: TZ_RRule,
+}
+
/*
A type representing datetime.
*/
DateTime :: struct {
using date: Date,
using time: Time,
+ tz: ^TZ_Region,
}
/*
@@ -130,4 +173,4 @@ Weekday :: enum i8 {
}
@(private)
-MONTH_DAYS :: [?]i8{-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} \ No newline at end of file
+MONTH_DAYS :: [?]i8{-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
diff --git a/core/time/datetime/datetime.odin b/core/time/datetime/datetime.odin
index fc9780e3b..2cd90b0e7 100644
--- a/core/time/datetime/datetime.odin
+++ b/core/time/datetime/datetime.odin
@@ -76,7 +76,7 @@ datetime, an error is returned.
components_to_datetime :: proc "contextless" (#any_int year, #any_int month, #any_int day, #any_int hour, #any_int minute, #any_int second: i64, #any_int nanos := i64(0)) -> (datetime: DateTime, err: Error) {
date := components_to_date(year, month, day) or_return
time := components_to_time(hour, minute, second, nanos) or_return
- return {date, time}, .None
+ return {date, time, nil}, .None
}
/*
@@ -88,7 +88,7 @@ object will always have the time equal to `00:00:00.000`.
*/
ordinal_to_datetime :: proc "contextless" (ordinal: Ordinal) -> (datetime: DateTime, err: Error) {
d := ordinal_to_date(ordinal) or_return
- return {Date(d), {}}, .None
+ return {Date(d), {}, nil}, .None
}
/*
@@ -433,4 +433,4 @@ unsafe_ordinal_to_date :: proc "contextless" (ordinal: Ordinal) -> (date: Date)
day := i8(ordinal - unsafe_date_to_ordinal(Date{year, month, 1}) + 1)
return {year, month, day}
-} \ No newline at end of file
+}
diff --git a/core/time/time.odin b/core/time/time.odin
index 98639b36a..b488f951c 100644
--- a/core/time/time.odin
+++ b/core/time/time.odin
@@ -930,7 +930,7 @@ If the datetime represents a time outside of a valid range, `false` is returned
as the second return value. See `Time` for the representable range.
*/
compound_to_time :: proc "contextless" (datetime: dt.DateTime) -> (t: Time, ok: bool) {
- unix_epoch := dt.DateTime{{1970, 1, 1}, {0, 0, 0, 0}}
+ unix_epoch := dt.DateTime{{1970, 1, 1}, {0, 0, 0, 0}, nil}
delta, err := dt.sub(datetime, unix_epoch)
if err != .None {
return
@@ -958,7 +958,7 @@ datetime_to_time :: proc{components_to_time, compound_to_time}
Convert time into datetime.
*/
time_to_datetime :: proc "contextless" (t: Time) -> (dt.DateTime, bool) {
- unix_epoch := dt.DateTime{{1970, 1, 1}, {0, 0, 0, 0}}
+ unix_epoch := dt.DateTime{{1970, 1, 1}, {0, 0, 0, 0}, nil}
datetime, err := dt.add(unix_epoch, dt.Delta{ nanos = t._nsec })
if err != .None {
diff --git a/core/time/time_linux.odin b/core/time/time_linux.odin
index 649f601dc..4e557766e 100644
--- a/core/time/time_linux.odin
+++ b/core/time/time_linux.odin
@@ -6,8 +6,8 @@ _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)}
+ ns := i64(time_spec_now.time_sec) * 1e9 + i64(time_spec_now.time_nsec)
+ return Time{_nsec=ns}
}
_sleep :: proc "contextless" (d: Duration) {
@@ -29,7 +29,7 @@ _sleep :: proc "contextless" (d: Duration) {
_tick_now :: proc "contextless" () -> Tick {
t, _ := linux.clock_gettime(.MONOTONIC_RAW)
- return Tick{_nsec = i64(t.time_sec*1e9 + t.time_nsec)}
+ return Tick{_nsec = i64(t.time_sec)*1e9 + i64(t.time_nsec)}
}
_yield :: proc "contextless" () {
diff --git a/core/time/timezone/tz_unix.odin b/core/time/timezone/tz_unix.odin
new file mode 100644
index 000000000..990e78d41
--- /dev/null
+++ b/core/time/timezone/tz_unix.odin
@@ -0,0 +1,89 @@
+#+build darwin, linux, freebsd, openbsd, netbsd
+#+private
+package timezone
+
+import "core:os"
+import "core:strings"
+import "core:path/filepath"
+import "core:time/datetime"
+
+local_tz_name :: proc(allocator := context.allocator) -> (name: string, success: bool) {
+ local_str, ok := os.lookup_env("TZ", allocator)
+ if !ok {
+ orig_localtime_path := "/etc/localtime"
+ path, err := os.absolute_path_from_relative(orig_localtime_path, allocator)
+ if err != nil {
+ // If we can't find /etc/localtime, fallback to UTC
+ if err == .ENOENT {
+ str, err2 := strings.clone("UTC", allocator)
+ if err2 != nil { return }
+ return str, true
+ }
+
+ return
+ }
+ defer delete(path, allocator)
+
+ // FreeBSD makes me sad.
+ // This is a hackaround, because FreeBSD copies rather than softlinks their local timezone file,
+ // *sometimes* and then stores the original name of the timezone in /var/db/zoneinfo instead
+ if path == orig_localtime_path {
+ data := os.read_entire_file("/var/db/zoneinfo", allocator) or_return
+ return strings.trim_right_space(string(data)), true
+ }
+
+ // Looking for tz path (ex fmt: "UTC", "Etc/UTC" or "America/Los_Angeles")
+ path_dir, path_file := filepath.split(path)
+ if path_dir == "" {
+ return
+ }
+ upper_path_dir, upper_path_chunk := filepath.split(path_dir[:len(path_dir)-1])
+ if upper_path_dir == "" {
+ return
+ }
+
+ if strings.contains(upper_path_chunk, "zoneinfo") {
+ region_str, err := strings.clone(path_file, allocator)
+ if err != nil { return }
+ return region_str, true
+ } else {
+ region_str, err := filepath.join({upper_path_chunk, path_file}, allocator = allocator)
+ if err != nil { return }
+ return region_str, true
+ }
+ }
+
+ if local_str == "" {
+ delete(local_str, allocator)
+
+ str, err := strings.clone("UTC", allocator)
+ if err != nil { return }
+ return str, true
+ }
+
+ return local_str, true
+}
+
+_region_load :: proc(_reg_str: string, allocator := context.allocator) -> (out_reg: ^datetime.TZ_Region, success: bool) {
+ reg_str := _reg_str
+ if reg_str == "UTC" {
+ return nil, true
+ }
+
+ if reg_str == "local" {
+ local_name := local_tz_name(allocator) or_return
+ if local_name == "UTC" {
+ delete(local_name, allocator)
+ return nil, true
+ }
+
+ reg_str = local_name
+ }
+ defer if _reg_str == "local" { delete(reg_str, allocator) }
+
+ db_path := "/usr/share/zoneinfo"
+ region_path := filepath.join({db_path, reg_str}, allocator)
+ defer delete(region_path, allocator)
+
+ return load_tzif_file(region_path, reg_str, allocator)
+}
diff --git a/core/time/timezone/tz_windows.odin b/core/time/timezone/tz_windows.odin
new file mode 100644
index 000000000..238c4c933
--- /dev/null
+++ b/core/time/timezone/tz_windows.odin
@@ -0,0 +1,311 @@
+#+build windows
+#+private
+package timezone
+
+import "core:strings"
+import "core:sys/windows"
+import "core:time/datetime"
+
+TZ_Abbrev :: struct {
+ std: string,
+ dst: string,
+}
+
+tz_abbrevs := map[string]TZ_Abbrev {
+ "Egypt Standard Time" = {"EET", "EEST"}, // Africa/Cairo
+ "Morocco Standard Time" = {"+00", "+01"}, // Africa/Casablanca
+ "South Africa Standard Time" = {"SAST", "SAST"}, // Africa/Johannesburg
+ "South Sudan Standard Time" = {"CAT", "CAT"}, // Africa/Juba
+ "Sudan Standard Time" = {"CAT", "CAT"}, // Africa/Khartoum
+ "W. Central Africa Standard Time" = {"WAT", "WAT"}, // Africa/Lagos
+ "E. Africa Standard Time" = {"EAT", "EAT"}, // Africa/Nairobi
+ "Sao Tome Standard Time" = {"GMT", "GMT"}, // Africa/Sao_Tome
+ "Libya Standard Time" = {"EET", "EET"}, // Africa/Tripoli
+ "Namibia Standard Time" = {"CAT", "CAT"}, // Africa/Windhoek
+ "Aleutian Standard Time" = {"HST", "HDT"}, // America/Adak
+ "Alaskan Standard Time" = {"AKST", "AKDT"}, // America/Anchorage
+ "Tocantins Standard Time" = {"-03", "-03"}, // America/Araguaina
+ "Paraguay Standard Time" = {"-04", "-03"}, // America/Asuncion
+ "Bahia Standard Time" = {"-03", "-03"}, // America/Bahia
+ "SA Pacific Standard Time" = {"-05", "-05"}, // America/Bogota
+ "Argentina Standard Time" = {"-03", "-03"}, // America/Buenos_Aires
+ "Eastern Standard Time (Mexico)" = {"EST", "EST"}, // America/Cancun
+ "Venezuela Standard Time" = {"-04", "-04"}, // America/Caracas
+ "SA Eastern Standard Time" = {"-03", "-03"}, // America/Cayenne
+ "Central Standard Time" = {"CST", "CDT"}, // America/Chicago
+ "Central Brazilian Standard Time" = {"-04", "-04"}, // America/Cuiaba
+ "Mountain Standard Time" = {"MST", "MDT"}, // America/Denver
+ "Greenland Standard Time" = {"-03", "-02"}, // America/Godthab
+ "Turks And Caicos Standard Time" = {"EST", "EDT"}, // America/Grand_Turk
+ "Central America Standard Time" = {"CST", "CST"}, // America/Guatemala
+ "Atlantic Standard Time" = {"AST", "ADT"}, // America/Halifax
+ "Cuba Standard Time" = {"CST", "CDT"}, // America/Havana
+ "US Eastern Standard Time" = {"EST", "EDT"}, // America/Indianapolis
+ "SA Western Standard Time" = {"-04", "-04"}, // America/La_Paz
+ "Pacific Standard Time" = {"PST", "PDT"}, // America/Los_Angeles
+ "Mountain Standard Time (Mexico)" = {"MST", "MST"}, // America/Mazatlan
+ "Central Standard Time (Mexico)" = {"CST", "CST"}, // America/Mexico_City
+ "Saint Pierre Standard Time" = {"-03", "-02"}, // America/Miquelon
+ "Montevideo Standard Time" = {"-03", "-03"}, // America/Montevideo
+ "Eastern Standard Time" = {"EST", "EDT"}, // America/New_York
+ "US Mountain Standard Time" = {"MST", "MST"}, // America/Phoenix
+ "Haiti Standard Time" = {"EST", "EDT"}, // America/Port-au-Prince
+ "Magallanes Standard Time" = {"-03", "-03"}, // America/Punta_Arenas
+ "Canada Central Standard Time" = {"CST", "CST"}, // America/Regina
+ "Pacific SA Standard Time" = {"-04", "-03"}, // America/Santiago
+ "E. South America Standard Time" = {"-03", "-03"}, // America/Sao_Paulo
+ "Newfoundland Standard Time" = {"NST", "NDT"}, // America/St_Johns
+ "Pacific Standard Time (Mexico)" = {"PST", "PDT"}, // America/Tijuana
+ "Yukon Standard Time" = {"MST", "MST"}, // America/Whitehorse
+ "Central Asia Standard Time" = {"+06", "+06"}, // Asia/Almaty
+ "Jordan Standard Time" = {"+03", "+03"}, // Asia/Amman
+ "Arabic Standard Time" = {"+03", "+03"}, // Asia/Baghdad
+ "Azerbaijan Standard Time" = {"+04", "+04"}, // Asia/Baku
+ "SE Asia Standard Time" = {"+07", "+07"}, // Asia/Bangkok
+ "Altai Standard Time" = {"+07", "+07"}, // Asia/Barnaul
+ "Middle East Standard Time" = {"EET", "EEST"}, // Asia/Beirut
+ "India Standard Time" = {"IST", "IST"}, // Asia/Calcutta
+ "Transbaikal Standard Time" = {"+09", "+09"}, // Asia/Chita
+ "Sri Lanka Standard Time" = {"+0530", "+0530"}, // Asia/Colombo
+ "Syria Standard Time" = {"+03", "+03"}, // Asia/Damascus
+ "Bangladesh Standard Time" = {"+06", "+06"}, // Asia/Dhaka
+ "Arabian Standard Time" = {"+04", "+04"}, // Asia/Dubai
+ "West Bank Standard Time" = {"EET", "EEST"}, // Asia/Hebron
+ "W. Mongolia Standard Time" = {"+07", "+07"}, // Asia/Hovd
+ "North Asia East Standard Time" = {"+08", "+08"}, // Asia/Irkutsk
+ "Israel Standard Time" = {"IST", "IDT"}, // Asia/Jerusalem
+ "Afghanistan Standard Time" = {"+0430", "+0430"}, // Asia/Kabul
+ "Russia Time Zone 11" = {"+12", "+12"}, // Asia/Kamchatka
+ "Pakistan Standard Time" = {"PKT", "PKT"}, // Asia/Karachi
+ "Nepal Standard Time" = {"+0545", "+0545"}, // Asia/Katmandu
+ "North Asia Standard Time" = {"+07", "+07"}, // Asia/Krasnoyarsk
+ "Magadan Standard Time" = {"+11", "+11"}, // Asia/Magadan
+ "N. Central Asia Standard Time" = {"+07", "+07"}, // Asia/Novosibirsk
+ "Omsk Standard Time" = {"+06", "+06"}, // Asia/Omsk
+ "North Korea Standard Time" = {"KST", "KST"}, // Asia/Pyongyang
+ "Qyzylorda Standard Time" = {"+05", "+05"}, // Asia/Qyzylorda
+ "Myanmar Standard Time" = {"+0630", "+0630"}, // Asia/Rangoon
+ "Arab Standard Time" = {"+03", "+03"}, // Asia/Riyadh
+ "Sakhalin Standard Time" = {"+11", "+11"}, // Asia/Sakhalin
+ "Korea Standard Time" = {"KST", "KST"}, // Asia/Seoul
+ "China Standard Time" = {"CST", "CST"}, // Asia/Shanghai
+ "Singapore Standard Time" = {"+08", "+08"}, // Asia/Singapore
+ "Russia Time Zone 10" = {"+11", "+11"}, // Asia/Srednekolymsk
+ "Taipei Standard Time" = {"CST", "CST"}, // Asia/Taipei
+ "West Asia Standard Time" = {"+05", "+05"}, // Asia/Tashkent
+ "Georgian Standard Time" = {"+04", "+04"}, // Asia/Tbilisi
+ "Iran Standard Time" = {"+0330", "+0330"}, // Asia/Tehran
+ "Tokyo Standard Time" = {"JST", "JST"}, // Asia/Tokyo
+ "Tomsk Standard Time" = {"+07", "+07"}, // Asia/Tomsk
+ "Ulaanbaatar Standard Time" = {"+08", "+08"}, // Asia/Ulaanbaatar
+ "Vladivostok Standard Time" = {"+10", "+10"}, // Asia/Vladivostok
+ "Yakutsk Standard Time" = {"+09", "+09"}, // Asia/Yakutsk
+ "Ekaterinburg Standard Time" = {"+05", "+05"}, // Asia/Yekaterinburg
+ "Caucasus Standard Time" = {"+04", "+04"}, // Asia/Yerevan
+ "Azores Standard Time" = {"-01", "+00"}, // Atlantic/Azores
+ "Cape Verde Standard Time" = {"-01", "-01"}, // Atlantic/Cape_Verde
+ "Greenwich Standard Time" = {"GMT", "GMT"}, // Atlantic/Reykjavik
+ "Cen. Australia Standard Time" = {"ACST", "ACDT"}, // Australia/Adelaide
+ "E. Australia Standard Time" = {"AEST", "AEST"}, // Australia/Brisbane
+ "AUS Central Standard Time" = {"ACST", "ACST"}, // Australia/Darwin
+ "Aus Central W. Standard Time" = {"+0845", "+0845"}, // Australia/Eucla
+ "Tasmania Standard Time" = {"AEST", "AEDT"}, // Australia/Hobart
+ "Lord Howe Standard Time" = {"+1030", "+11"}, // Australia/Lord_Howe
+ "W. Australia Standard Time" = {"AWST", "AWST"}, // Australia/Perth
+ "AUS Eastern Standard Time" = {"AEST", "AEDT"}, // Australia/Sydney
+ "UTC-11" = {"-11", "-11"}, // Etc/GMT+11
+ "Dateline Standard Time" = {"-12", "-12"}, // Etc/GMT+12
+ "UTC-02" = {"-02", "-02"}, // Etc/GMT+2
+ "UTC-08" = {"-08", "-08"}, // Etc/GMT+8
+ "UTC-09" = {"-09", "-09"}, // Etc/GMT+9
+ "UTC+12" = {"+12", "+12"}, // Etc/GMT-12
+ "UTC+13" = {"+13", "+13"}, // Etc/GMT-13
+ "UTC" = {"UTC", "UTC"}, // Etc/UTC
+ "Astrakhan Standard Time" = {"+04", "+04"}, // Europe/Astrakhan
+ "W. Europe Standard Time" = {"CET", "CEST"}, // Europe/Berlin
+ "GTB Standard Time" = {"EET", "EEST"}, // Europe/Bucharest
+ "Central Europe Standard Time" = {"CET", "CEST"}, // Europe/Budapest
+ "E. Europe Standard Time" = {"EET", "EEST"}, // Europe/Chisinau
+ "Turkey Standard Time" = {"+03", "+03"}, // Europe/Istanbul
+ "Kaliningrad Standard Time" = {"EET", "EET"}, // Europe/Kaliningrad
+ "FLE Standard Time" = {"EET", "EEST"}, // Europe/Kiev
+ "GMT Standard Time" = {"GMT", "BST"}, // Europe/London
+ "Belarus Standard Time" = {"+03", "+03"}, // Europe/Minsk
+ "Russian Standard Time" = {"MSK", "MSK"}, // Europe/Moscow
+ "Romance Standard Time" = {"CET", "CEST"}, // Europe/Paris
+ "Russia Time Zone 3" = {"+04", "+04"}, // Europe/Samara
+ "Saratov Standard Time" = {"+04", "+04"}, // Europe/Saratov
+ "Volgograd Standard Time" = {"MSK", "MSK"}, // Europe/Volgograd
+ "Central European Standard Time" = {"CET", "CEST"}, // Europe/Warsaw
+ "Mauritius Standard Time" = {"+04", "+04"}, // Indian/Mauritius
+ "Samoa Standard Time" = {"+13", "+13"}, // Pacific/Apia
+ "New Zealand Standard Time" = {"NZST", "NZDT"}, // Pacific/Auckland
+ "Bougainville Standard Time" = {"+11", "+11"}, // Pacific/Bougainville
+ "Chatham Islands Standard Time" = {"+1245", "+1345"}, // Pacific/Chatham
+ "Easter Island Standard Time" = {"-06", "-05"}, // Pacific/Easter
+ "Fiji Standard Time" = {"+12", "+12"}, // Pacific/Fiji
+ "Central Pacific Standard Time" = {"+11", "+11"}, // Pacific/Guadalcanal
+ "Hawaiian Standard Time" = {"HST", "HST"}, // Pacific/Honolulu
+ "Line Islands Standard Time" = {"+14", "+14"}, // Pacific/Kiritimati
+ "Marquesas Standard Time" = {"-0930", "-0930"}, // Pacific/Marquesas
+ "Norfolk Standard Time" = {"+11", "+12"}, // Pacific/Norfolk
+ "West Pacific Standard Time" = {"+10", "+10"}, // Pacific/Port_Moresby
+ "Tonga Standard Time" = {"+13", "+13"}, // Pacific/Tongatapu
+}
+
+iana_to_windows_tz :: proc(iana_name: string, allocator := context.allocator) -> (name: string, success: bool) {
+ wintz_name_buffer: [128]u16
+ status: windows.UError
+
+ iana_name_wstr := windows.utf8_to_wstring(iana_name, allocator)
+ defer free(iana_name_wstr, allocator)
+
+ wintz_name_len := windows.ucal_getWindowsTimeZoneID(iana_name_wstr, -1, raw_data(wintz_name_buffer[:]), len(wintz_name_buffer), &status)
+ if status != .U_ZERO_ERROR {
+ return
+ }
+
+ wintz_name, err := windows.utf16_to_utf8(wintz_name_buffer[:wintz_name_len], allocator)
+ if err != nil {
+ return
+ }
+
+ return wintz_name, true
+}
+
+local_tz_name :: proc(allocator := context.allocator) -> (name: string, success: bool) {
+ iana_name_buffer: [128]u16
+ status: windows.UError
+
+ zone_str_len := windows.ucal_getDefaultTimeZone(raw_data(iana_name_buffer[:]), len(iana_name_buffer), &status)
+ if status != .U_ZERO_ERROR {
+ return
+ }
+
+ iana_name, err := windows.utf16_to_utf8(iana_name_buffer[:zone_str_len], allocator)
+ if err != nil {
+ return
+ }
+
+ return iana_name, true
+}
+
+REG_TZI_FORMAT :: struct #packed {
+ bias: windows.LONG,
+ std_bias: windows.LONG,
+ dst_bias: windows.LONG,
+ std_date: windows.SYSTEMTIME,
+ dst_date: windows.SYSTEMTIME,
+}
+
+generate_rrule_from_tzi :: proc(tzi: ^REG_TZI_FORMAT, abbrevs: TZ_Abbrev, allocator := context.allocator) -> (rrule: datetime.TZ_RRule, ok: bool) {
+ std_name, err := strings.clone(abbrevs.std, allocator)
+ if err != nil { return }
+ defer if err != nil { delete(std_name, allocator) }
+
+ if (tzi.std_date.month == 0) {
+ return datetime.TZ_RRule{
+ has_dst = false,
+
+ std_name = std_name,
+ std_offset = -(i64(tzi.bias) + i64(tzi.std_bias)) * 60,
+ dst_date = datetime.TZ_Transition_Date{
+ type = .Month_Week_Day,
+ month = u8(tzi.std_date.month),
+ week = u8(tzi.std_date.day),
+ day = tzi.std_date.day_of_week,
+ time = (i64(tzi.std_date.hour) * 60 * 60) + (i64(tzi.std_date.minute) * 60) + i64(tzi.std_date.second),
+ },
+ }, true
+ }
+
+ dst_name: string
+ dst_name, err = strings.clone(abbrevs.dst, allocator)
+ if err != nil { return }
+ defer if err != nil { delete(dst_name, allocator) }
+
+ return datetime.TZ_RRule{
+ has_dst = true,
+
+ std_name = std_name,
+ std_offset = -(i64(tzi.bias) + i64(tzi.std_bias)) * 60,
+ dst_date = datetime.TZ_Transition_Date{
+ type = .Month_Week_Day,
+ month = u8(tzi.std_date.month),
+ week = u8(tzi.std_date.day),
+ day = tzi.std_date.day_of_week,
+ time = (i64(tzi.std_date.hour) * 60 * 60) + (i64(tzi.std_date.minute) * 60) + i64(tzi.std_date.second),
+ },
+
+ dst_name = dst_name,
+ dst_offset = -(i64(tzi.bias) + i64(tzi.dst_bias)) * 60,
+ std_date = datetime.TZ_Transition_Date{
+ type = .Month_Week_Day,
+ month = u8(tzi.dst_date.month),
+ week = u8(tzi.dst_date.day),
+ day = tzi.dst_date.day_of_week,
+ time = (i64(tzi.dst_date.hour) * 60 * 60) + (i64(tzi.dst_date.minute) * 60) + i64(tzi.dst_date.second),
+ },
+ }, true
+}
+
+_region_load :: proc(reg_str: string, allocator := context.allocator) -> (out_reg: ^datetime.TZ_Region, success: bool) {
+ wintz_name: string
+ iana_name: string
+
+ if reg_str == "local" {
+ ok := false
+
+ iana_name = local_tz_name(allocator) or_return
+ wintz_name, ok = iana_to_windows_tz(iana_name, allocator)
+ if !ok {
+ delete(iana_name, allocator)
+ return
+ }
+ } else {
+ wintz_name = iana_to_windows_tz(reg_str, allocator) or_return
+ iana_name = strings.clone(reg_str, allocator)
+ }
+ defer delete(wintz_name, allocator)
+ defer delete(iana_name, allocator)
+
+ abbrevs := tz_abbrevs[wintz_name] or_return
+ if abbrevs.std == "UTC" && abbrevs.dst == abbrevs.std {
+ return nil, true
+ }
+
+ key_base := `SOFTWARE\Microsoft\Windows NT\CurrentVersion\Time Zones`
+ tz_key := strings.join({key_base, wintz_name}, "\\", allocator = allocator)
+ defer delete(tz_key, allocator)
+
+ tz_key_wstr := windows.utf8_to_wstring(tz_key, allocator)
+ defer free(tz_key_wstr, allocator)
+
+ key: windows.HKEY
+ res := windows.RegOpenKeyExW(windows.HKEY_LOCAL_MACHINE, tz_key_wstr, 0, windows.KEY_READ, &key)
+ if res != 0 { return }
+ defer windows.RegCloseKey(key)
+
+ tzi: REG_TZI_FORMAT
+ size := u32(size_of(REG_TZI_FORMAT))
+
+ res = windows.RegGetValueW(key, nil, windows.L("TZI"), windows.RRF_RT_ANY, nil, &tzi, &size)
+ if res != 0 {
+ return
+ }
+
+ rrule := generate_rrule_from_tzi(&tzi, abbrevs, allocator) or_return
+
+ region_name, err := strings.clone(iana_name, allocator)
+ if err != nil { return }
+ defer if err != nil { delete(region_name, allocator) }
+
+ region: ^datetime.TZ_Region
+ region, err = new_clone(datetime.TZ_Region{
+ name = region_name,
+ rrule = rrule,
+ }, allocator)
+ if err != nil { return }
+
+ return region, true
+}
diff --git a/core/time/timezone/tzdate.odin b/core/time/timezone/tzdate.odin
new file mode 100644
index 000000000..96df44299
--- /dev/null
+++ b/core/time/timezone/tzdate.odin
@@ -0,0 +1,341 @@
+package timezone
+
+import "core:fmt"
+import "core:slice"
+import "core:time"
+import "core:time/datetime"
+
+region_load :: proc(reg: string, allocator := context.allocator) -> (out_reg: ^datetime.TZ_Region, ok: bool) {
+ return _region_load(reg, allocator)
+}
+
+region_load_from_file :: proc(file_path, reg: string, allocator := context.allocator) -> (out_reg: ^datetime.TZ_Region, ok: bool) {
+ return load_tzif_file(file_path, reg, allocator)
+}
+
+region_load_from_buffer :: proc(buffer: []u8, reg: string, allocator := context.allocator) -> (out_reg: ^datetime.TZ_Region, ok: bool) {
+ return parse_tzif(buffer, reg, allocator)
+}
+
+rrule_destroy :: proc(rrule: datetime.TZ_RRule, allocator := context.allocator) {
+ delete(rrule.std_name, allocator)
+ delete(rrule.dst_name, allocator)
+}
+
+region_destroy :: proc(region: ^datetime.TZ_Region, allocator := context.allocator) {
+ if region == nil {
+ return
+ }
+
+ for name in region.shortnames {
+ delete(name, allocator)
+ }
+ delete(region.shortnames, allocator)
+ delete(region.records, allocator)
+ delete(region.name, allocator)
+ rrule_destroy(region.rrule, allocator)
+ free(region, allocator)
+}
+
+
+@private
+region_get_nearest :: proc(region: ^datetime.TZ_Region, tm: time.Time) -> (out: datetime.TZ_Record, success: bool) {
+ if len(region.records) == 0 {
+ return process_rrule(region.rrule, tm)
+ }
+
+ n := len(region.records)
+ left, right := 0, n
+
+ tm_sec := time.to_unix_seconds(tm)
+ last_time := region.records[len(region.records)-1].time
+ if tm_sec > last_time {
+ return process_rrule(region.rrule, tm)
+ }
+
+ for left < right {
+ mid := int(uint(left+right) >> 1)
+ if region.records[mid].time < tm_sec {
+ left = mid + 1
+ } else {
+ right = mid
+ }
+ }
+
+ idx := max(0, left-1)
+ return region.records[idx], true
+}
+
+@private
+month_to_seconds :: proc(month: int, is_leap: bool) -> i64 {
+ month_seconds := []i64{
+ 0, 31 * 86_400, 59 * 86_400, 90 * 86_400,
+ 120 * 86_400, 151 * 86_400, 181 * 86_400, 212 * 86_400,
+ 243 * 86_400, 273 * 86_400, 304 * 86_400, 334 * 86_400,
+ }
+
+ t := month_seconds[month]
+ if is_leap && month >= 2 {
+ t += 86_400
+ }
+ return t
+}
+
+@private
+trans_date_to_seconds :: proc(year: i64, td: datetime.TZ_Transition_Date) -> (secs: i64, ok: bool) {
+ is_leap := datetime.is_leap_year(year)
+ DAY_SEC :: 86_400
+
+ year_start := datetime.DateTime{{year, 1, 1}, {0, 0, 0, 0}, nil}
+ year_start_time := time.datetime_to_time(year_start) or_return
+
+ t := i64(time.to_unix_seconds(year_start_time))
+
+ switch td.type {
+ case .Month_Week_Day:
+ if td.month < 1 { return }
+
+ t += month_to_seconds(int(td.month) - 1, is_leap)
+
+ weekday := ((t + (4 * DAY_SEC)) %% (7 * DAY_SEC)) / DAY_SEC
+ days := i64(td.day) - weekday
+ if days < 0 { days += 7 }
+
+ month_daycount, err := datetime.last_day_of_month(year, td.month)
+ if err != nil { return }
+
+ week := td.week
+ if week == 5 && days + 28 >= i64(month_daycount) {
+ week = 4
+ }
+
+ t += DAY_SEC * (days + (7 * i64(week - 1)))
+ t += td.time
+
+ return t, true
+
+ // Both of these should result in 0 -> 365 days (in seconds)
+ case .No_Leap:
+ day := i64(td.day)
+
+ // if before Feb 29th || not a leap year
+ if day < 60 || !is_leap {
+ day -= 1
+ }
+ t += DAY_SEC * day
+
+ return t, true
+
+ case .Leap:
+ t += DAY_SEC * i64(td.day)
+
+ return t, true
+
+ case:
+ return
+ }
+
+ return
+}
+
+@private
+process_rrule :: proc(rrule: datetime.TZ_RRule, tm: time.Time) -> (out: datetime.TZ_Record, success: bool) {
+ if !rrule.has_dst {
+ return datetime.TZ_Record{
+ time = time.to_unix_seconds(tm),
+ utc_offset = rrule.std_offset,
+ shortname = rrule.std_name,
+ dst = false,
+ }, true
+ }
+
+ y, _, _ := time.date(tm)
+ std_secs := trans_date_to_seconds(i64(y), rrule.std_date) or_return
+ dst_secs := trans_date_to_seconds(i64(y), rrule.dst_date) or_return
+
+ records := []datetime.TZ_Record{
+ {
+ time = std_secs,
+ utc_offset = rrule.std_offset,
+ shortname = rrule.std_name,
+ dst = false,
+ },
+ {
+ time = dst_secs,
+ utc_offset = rrule.dst_offset,
+ shortname = rrule.dst_name,
+ dst = true,
+ },
+ }
+ record_sort_proc :: proc(i, j: datetime.TZ_Record) -> bool {
+ return i.time > j.time
+ }
+ slice.sort_by(records, record_sort_proc)
+
+ tm_sec := time.to_unix_seconds(tm)
+ for record in records {
+ if tm_sec < record.time {
+ return record, true
+ }
+ }
+
+ return records[len(records)-1], true
+}
+
+datetime_to_utc :: proc(dt: datetime.DateTime) -> (out: datetime.DateTime, success: bool) #optional_ok {
+ if dt.tz == nil {
+ return dt, true
+ }
+
+ tm := time.datetime_to_time(dt) or_return
+ record := region_get_nearest(dt.tz, tm) or_return
+
+ secs := time.time_to_unix(tm)
+ adj_time := time.unix(secs - record.utc_offset, 0)
+ adj_dt := time.time_to_datetime(adj_time) or_return
+ return adj_dt, true
+}
+
+/*
+Converts a datetime on one timezone to another timezone
+
+Inputs:
+- dt: The input datetime
+- tz: The timezone to convert to
+
+NOTE: tz will be referenced in the result datetime, so it must stay alive/allocated as long as it is used
+Returns:
+- out: The converted datetime
+- success: `false` if the datetime was invalid
+*/
+datetime_to_tz :: proc(dt: datetime.DateTime, tz: ^datetime.TZ_Region) -> (out: datetime.DateTime, success: bool) #optional_ok {
+ dt := dt
+ if dt.tz == tz {
+ return dt, true
+ }
+ if dt.tz != nil {
+ dt = datetime_to_utc(dt)
+ }
+ if tz == nil {
+ return dt, true
+ }
+
+ tm := time.datetime_to_time(dt) or_return
+ record := region_get_nearest(tz, tm) or_return
+
+ secs := time.time_to_unix(tm)
+ adj_time := time.unix(secs + record.utc_offset, 0)
+ adj_dt := time.time_to_datetime(adj_time) or_return
+ adj_dt.tz = tz
+
+ return adj_dt, true
+}
+
+/*
+Gets the timezone abbreviation/shortname for a given date.
+(ex: "PDT")
+
+Inputs:
+- dt: The datetime containing the date, time, and timezone pointer for the lookup
+
+NOTE: The lifetime of name matches the timezone it was pulled from.
+Returns:
+- name: The timezone abbreviation
+- success: returns `false` if the passed datetime is invalid
+*/
+shortname :: proc(dt: datetime.DateTime) -> (name: string, success: bool) #optional_ok {
+ tm := time.datetime_to_time(dt) or_return
+ if dt.tz == nil { return "UTC", true }
+
+ record := region_get_nearest(dt.tz, tm) or_return
+ return record.shortname, true
+}
+
+/*
+Gets the timezone abbreviation/shortname for a given date.
+(ex: "PDT")
+
+WARNING: This is unsafe because it doesn't check if your datetime is valid or if your region contains a valid record.
+
+Inputs:
+- dt: The input datetime
+
+NOTE: The lifetime of name matches the timezone it was pulled from.
+Returns:
+- name: The timezone abbreviation
+*/
+shortname_unsafe :: proc(dt: datetime.DateTime) -> string {
+ if dt.tz == nil { return "UTC" }
+
+ tm, _ := time.datetime_to_time(dt)
+ record, _ := region_get_nearest(dt.tz, tm)
+ return record.shortname
+}
+
+/*
+Checks DST for a given date.
+
+Inputs:
+- dt: The input datetime
+
+Returns:
+- is_dst: returns `true` if dt is in daylight savings time, `false` if not
+- success: returns `false` if the passed datetime is invalid
+*/
+dst :: proc(dt: datetime.DateTime) -> (is_dst: bool, success: bool) #optional_ok {
+ tm := time.datetime_to_time(dt) or_return
+ if dt.tz == nil { return false, true }
+
+ record := region_get_nearest(dt.tz, tm) or_return
+ return record.dst, true
+}
+
+/*
+Checks DST for a given date.
+
+WARNING: This is unsafe because it doesn't check if your datetime is valid or if your region contains a valid record.
+
+Inputs:
+- dt: The input datetime
+
+Returns:
+- is_dst: returns `true` if dt is in daylight savings time, `false` if not
+*/
+dst_unsafe :: proc(dt: datetime.DateTime) -> bool {
+ if dt.tz == nil { return false }
+
+ tm, _ := time.datetime_to_time(dt)
+ record, _ := region_get_nearest(dt.tz, tm)
+ return record.dst
+}
+
+datetime_to_str :: proc(dt: datetime.DateTime, allocator := context.allocator) -> string {
+ if dt.tz == nil {
+ _, ok := time.datetime_to_time(dt)
+ if !ok {
+ return ""
+ }
+
+ return fmt.aprintf("%02d-%02d-%04d @ %02d:%02d:%02d UTC", dt.month, dt.day, dt.year, dt.hour, dt.minute, dt.second, allocator = allocator)
+
+ } else {
+ tm, ok := time.datetime_to_time(dt)
+ if !ok {
+ return ""
+ }
+
+ record, ok2 := region_get_nearest(dt.tz, tm)
+ if !ok2 {
+ return ""
+ }
+
+ hour := dt.hour
+ am_pm_str := "AM"
+ if hour > 12 {
+ am_pm_str = "PM"
+ hour -= 12
+ }
+
+ return fmt.aprintf("%02d-%02d-%04d @ %02d:%02d:%02d %s %s", dt.month, dt.day, dt.year, hour, dt.minute, dt.second, am_pm_str, record.shortname, allocator = allocator)
+ }
+}
diff --git a/core/time/timezone/tzif.odin b/core/time/timezone/tzif.odin
new file mode 100644
index 000000000..609cbda73
--- /dev/null
+++ b/core/time/timezone/tzif.odin
@@ -0,0 +1,652 @@
+package timezone
+
+import "base:intrinsics"
+
+import "core:slice"
+import "core:strings"
+import "core:os"
+import "core:strconv"
+import "core:time/datetime"
+
+// Implementing RFC8536 [https://datatracker.ietf.org/doc/html/rfc8536]
+
+TZIF_MAGIC :: u32be(0x545A6966) // 'TZif'
+TZif_Version :: enum u8 {
+ V1 = 0,
+ V2 = '2',
+ V3 = '3',
+ V4 = '4',
+}
+BIG_BANG_ISH :: -0x800000000000000
+
+TZif_Header :: struct #packed {
+ magic: u32be,
+ version: TZif_Version,
+ reserved: [15]u8,
+ isutcnt: u32be,
+ isstdcnt: u32be,
+ leapcnt: u32be,
+ timecnt: u32be,
+ typecnt: u32be,
+ charcnt: u32be,
+}
+
+Sun_Shift :: enum u8 {
+ Standard = 0,
+ DST = 1,
+}
+
+Local_Time_Type :: struct #packed {
+ utoff: i32be,
+ dst: Sun_Shift,
+ idx: u8,
+}
+
+Leapsecond_Record :: struct #packed {
+ occur: i64be,
+ corr: i32be,
+}
+
+@private
+tzif_data_block_size :: proc(hdr: ^TZif_Header, version: TZif_Version) -> (block_size: int, ok: bool) {
+ time_size : int
+
+ if version == .V1 {
+ time_size = 4
+ } else if version == .V2 || version == .V3 || version == .V4 {
+ time_size = 8
+ } else {
+ return
+ }
+
+ return (int(hdr.timecnt) * time_size) +
+ int(hdr.timecnt) +
+ int(hdr.typecnt * size_of(Local_Time_Type)) +
+ int(hdr.charcnt) +
+ (int(hdr.leapcnt) * (time_size + 4)) +
+ int(hdr.isstdcnt) +
+ int(hdr.isutcnt), true
+}
+
+
+load_tzif_file :: proc(filename: string, region_name: string, allocator := context.allocator) -> (out: ^datetime.TZ_Region, ok: bool) {
+ tzif_data := os.read_entire_file_from_filename(filename, allocator) or_return
+ defer delete(tzif_data, allocator)
+ return parse_tzif(tzif_data, region_name, allocator)
+}
+
+@private
+is_alphabetic :: proc(ch: u8) -> bool {
+ // ('A' -> 'Z') || ('a' -> 'z')
+ return (ch > 0x40 && ch < 0x5B) || (ch > 0x60 && ch < 0x7B)
+}
+
+@private
+is_numeric :: proc(ch: u8) -> bool {
+ // ('0' -> '9')
+ return (ch > 0x2F && ch < 0x3A)
+}
+
+@private
+is_alphanumeric :: proc(ch: u8) -> bool {
+ return is_alphabetic(ch) || is_numeric(ch)
+}
+
+@private
+is_valid_quoted_char :: proc(ch: u8) -> bool {
+ return is_alphabetic(ch) || is_numeric(ch) || ch == '+' || ch == '-'
+}
+
+@private
+parse_posix_tz_shortname :: proc(str: string) -> (out: string, idx: int, ok: bool) {
+ was_quoted := false
+ quoted := false
+ i := 0
+
+ for ; i < len(str); i += 1 {
+ ch := str[i]
+
+ if !quoted && ch == '<' {
+ quoted = true
+ was_quoted = true
+ continue
+ }
+
+ if quoted && ch == '>' {
+ quoted = false
+ break
+ }
+
+ if !is_valid_quoted_char(ch) && ch != ',' {
+ return
+ }
+
+ if !quoted && !is_alphabetic(ch) {
+ break
+ }
+ }
+
+ // If we didn't see the trailing quote
+ if was_quoted && quoted {
+ return
+ }
+
+ out_str: string
+ end_idx := i
+ if was_quoted {
+ end_idx += 1
+ out_str = str[1:i]
+ } else {
+ out_str = str[:i]
+ }
+
+ return out_str, end_idx, true
+}
+
+@private
+parse_posix_tz_offset :: proc(str: string) -> (out_sec: i64, idx: int, ok: bool) {
+ str := str
+
+ sign : i64 = 1
+ start_idx := 0
+ i := 0
+ if str[i] == '+' {
+ i += 1
+ sign = 1
+ start_idx = 1
+ } else if str[i] == '-' {
+ i += 1
+ sign = -1
+ start_idx = 1
+ }
+
+ got_more_time := false
+ for ; i < len(str); i += 1 {
+ if is_numeric(str[i]) {
+ continue
+ }
+
+ if str[i] == ':' {
+ got_more_time = true
+ break
+ }
+
+ break
+ }
+
+ ret_sec : i64 = 0
+ hours := strconv.parse_int(str[start_idx:i], 10) or_return
+ if hours > 167 || hours < -167 {
+ return
+ }
+ ret_sec += i64(hours) * (60 * 60)
+ if !got_more_time {
+ return ret_sec * sign, i, true
+ }
+
+ i += 1
+ start_idx = i
+
+ got_more_time = false
+ for ; i < len(str); i += 1 {
+ if is_numeric(str[i]) {
+ continue
+ }
+
+ if str[i] == ':' {
+ got_more_time = true
+ break
+ }
+
+ break
+ }
+
+ mins_str := str[start_idx:i]
+ if len(mins_str) != 2 {
+ return
+ }
+
+ mins := strconv.parse_int(mins_str, 10) or_return
+ if mins > 59 || mins < 0 {
+ return
+ }
+ ret_sec += i64(mins) * 60
+ if !got_more_time {
+ return ret_sec * sign, i, true
+ }
+
+ i += 1
+ start_idx = i
+
+ for ; i < len(str); i += 1 {
+ if !is_numeric(str[i]) {
+ break
+ }
+ }
+ secs_str := str[start_idx:i]
+ if len(secs_str) != 2 {
+ return
+ }
+
+ secs := strconv.parse_int(secs_str, 10) or_return
+ if secs > 59 || secs < 0 {
+ return
+ }
+ ret_sec += i64(secs)
+ return ret_sec * sign, i, true
+}
+
+@private
+skim_digits :: proc(str: string) -> (out: string, idx: int, ok: bool) {
+ i := 0
+ for ; i < len(str); i += 1 {
+ ch := str[i]
+ if ch == '.' || ch == '/' || ch == ',' {
+ break
+ }
+
+ if !is_numeric(ch) {
+ return
+ }
+ }
+
+ return str[:i], i, true
+}
+
+TWO_AM :: 2 * 60 * 60
+parse_posix_rrule :: proc(str: string) -> (out: datetime.TZ_Transition_Date, idx: int, ok: bool) {
+ str := str
+ if len(str) < 2 { return }
+
+ i := 0
+ // No leap
+ if str[i] == 'J' {
+ i += 1
+
+ day_str, off := skim_digits(str[i:]) or_return
+ i += off
+
+ day := strconv.parse_int(day_str, 10) or_return
+ if day < 1 || day > 365 { return }
+
+ offset : i64 = TWO_AM
+ if len(str) != i && str[i] == '/' {
+ i += 1
+
+ offset, off = parse_posix_tz_offset(str[i:]) or_return
+ i += off
+ }
+
+ if len(str) != i && str[i] == ',' {
+ i += 1
+ }
+
+ return datetime.TZ_Transition_Date{
+ type = .No_Leap,
+ day = u16(day),
+ time = offset,
+ }, i, true
+
+ // Leap
+ } else if is_numeric(str[i]) {
+ day_str, off := skim_digits(str[i:]) or_return
+ i += off
+
+ day := strconv.parse_int(day_str, 10) or_return
+ if day < 0 || day > 365 { return }
+
+ offset : i64 = TWO_AM
+ if len(str) != i && str[i] == '/' {
+ i += 1
+
+ offset, off = parse_posix_tz_offset(str[i:]) or_return
+ i += off
+ }
+
+ if len(str) != i && str[i] == ',' {
+ i += 1
+ }
+
+ return datetime.TZ_Transition_Date{
+ type = .Leap,
+ day = u16(day),
+ time = offset,
+ }, i, true
+
+ } else if str[i] == 'M' {
+ i += 1
+
+ month_str, week_str, day_str: string
+ off := 0
+
+ month_str, off = skim_digits(str[i:]) or_return
+ i += off + 1
+
+ week_str, off = skim_digits(str[i:]) or_return
+ i += off + 1
+
+ day_str, off = skim_digits(str[i:]) or_return
+ i += off
+
+ month := strconv.parse_int(month_str, 10) or_return
+ if month < 1 || month > 12 { return }
+
+ week := strconv.parse_int(week_str, 10) or_return
+ if week < 1 || week > 5 { return }
+
+ day := strconv.parse_int(day_str, 10) or_return
+ if day < 0 || day > 6 { return }
+
+ offset : i64 = TWO_AM
+ if len(str) != i && str[i] == '/' {
+ i += 1
+
+ offset, off = parse_posix_tz_offset(str[i:]) or_return
+ i += off
+ }
+
+ if len(str) != i && str[i] == ',' {
+ i += 1
+ }
+
+ return datetime.TZ_Transition_Date{
+ type = .Month_Week_Day,
+ month = u8(month),
+ week = u8(week),
+ day = u16(day),
+ time = offset,
+ }, i, true
+ }
+
+ return
+}
+
+parse_posix_tz :: proc(posix_tz: string, allocator := context.allocator) -> (out: datetime.TZ_RRule, ok: bool) {
+ // TZ string contain at least 3 characters for the STD name, and 1 for the offset
+ if len(posix_tz) < 4 {
+ return
+ }
+
+ str := posix_tz
+
+ std_name, idx := parse_posix_tz_shortname(str) or_return
+ str = str[idx:]
+
+ std_offset, idx2 := parse_posix_tz_offset(str) or_return
+ std_offset *= -1
+ str = str[idx2:]
+
+ std_name_str, err := strings.clone(std_name, allocator)
+ if err != nil { return }
+ defer if !ok { delete(std_name_str, allocator) }
+
+ if len(str) == 0 {
+ return datetime.TZ_RRule{
+ has_dst = false,
+ std_name = std_name_str,
+ std_offset = std_offset,
+ std_date = datetime.TZ_Transition_Date{
+ type = .Leap,
+ day = 0,
+ time = TWO_AM,
+ },
+ }, true
+ }
+
+ dst_name: string
+ dst_offset := std_offset + (1 * 60 * 60)
+ if str[0] != ',' {
+ dst_name, idx = parse_posix_tz_shortname(str) or_return
+ str = str[idx:]
+
+ if str[0] != ',' {
+ dst_offset, idx = parse_posix_tz_offset(str) or_return
+ dst_offset *= -1
+ str = str[idx:]
+ }
+ }
+ if str[0] != ',' { return }
+ str = str[1:]
+
+ std_td, idx3 := parse_posix_rrule(str) or_return
+ str = str[idx3:]
+
+ dst_td, idx4 := parse_posix_rrule(str) or_return
+ str = str[idx4:]
+
+ dst_name_str: string
+ dst_name_str, err = strings.clone(dst_name, allocator)
+ if err != nil { return }
+
+ return datetime.TZ_RRule{
+ has_dst = true,
+
+ std_name = std_name_str,
+ std_offset = std_offset,
+ std_date = std_td,
+
+ dst_name = dst_name_str,
+ dst_offset = dst_offset,
+ dst_date = dst_td,
+ }, true
+}
+
+parse_tzif :: proc(_buffer: []u8, region_name: string, allocator := context.allocator) -> (out: ^datetime.TZ_Region, ok: bool) {
+ context.allocator = allocator
+
+ buffer := _buffer
+
+ // TZif is crufty. Skip the initial header.
+
+ v1_hdr := slice.to_type(buffer, TZif_Header) or_return
+ if v1_hdr.magic != TZIF_MAGIC {
+ return
+ }
+ if v1_hdr.typecnt == 0 || v1_hdr.charcnt == 0 {
+ return
+ }
+ if v1_hdr.isutcnt != 0 && v1_hdr.isutcnt != v1_hdr.typecnt {
+ return
+ }
+ if v1_hdr.isstdcnt != 0 && v1_hdr.isstdcnt != v1_hdr.typecnt {
+ return
+ }
+
+ // We don't bother supporting v1, it uses u32 timestamps
+ if v1_hdr.version == .V1 {
+ return
+ }
+ // We only support v2 and v3
+ if v1_hdr.version != .V2 && v1_hdr.version != .V3 {
+ return
+ }
+
+ // Skip the initial v1 block too.
+ first_block_size, _ := tzif_data_block_size(&v1_hdr, .V1)
+ if len(buffer) <= size_of(v1_hdr) + first_block_size {
+ return
+ }
+ buffer = buffer[size_of(v1_hdr)+first_block_size:]
+
+ // Ok, time to parse real things
+ real_hdr := slice.to_type(buffer, TZif_Header) or_return
+ if real_hdr.magic != TZIF_MAGIC {
+ return
+ }
+ if real_hdr.typecnt == 0 || real_hdr.charcnt == 0 {
+ return
+ }
+ if real_hdr.isutcnt != 0 && real_hdr.isutcnt != real_hdr.typecnt {
+ return
+ }
+ if real_hdr.isstdcnt != 0 && real_hdr.isstdcnt != real_hdr.typecnt {
+ return
+ }
+
+ // Grab the real data block
+ real_block_size, _ := tzif_data_block_size(&real_hdr, v1_hdr.version)
+ if len(buffer) <= size_of(real_hdr) + real_block_size {
+ return
+ }
+ buffer = buffer[size_of(real_hdr):]
+
+ time_size := 8
+ transition_times := slice.reinterpret([]i64be, buffer[:int(real_hdr.timecnt)*size_of(i64be)])
+ for time in transition_times {
+ if time < BIG_BANG_ISH {
+ return
+ }
+ }
+ buffer = buffer[int(real_hdr.timecnt)*time_size:]
+
+ transition_types := buffer[:int(real_hdr.timecnt)]
+ for type in transition_types {
+ if int(type) > int(real_hdr.typecnt - 1) {
+ return
+ }
+ }
+ buffer = buffer[int(real_hdr.timecnt):]
+
+ local_time_types := slice.reinterpret([]Local_Time_Type, buffer[:int(real_hdr.typecnt)*size_of(Local_Time_Type)])
+ for ltt in local_time_types {
+ // UT offset should be > -25 hours and < 26 hours
+ if int(ltt.utoff) < -89999 || int(ltt.utoff) > 93599 {
+ return
+ }
+
+ if ltt.dst != .DST && ltt.dst != .Standard {
+ return
+ }
+
+ if int(ltt.idx) > int(real_hdr.charcnt - 1) {
+ return
+ }
+ }
+
+ buffer = buffer[int(real_hdr.typecnt) * size_of(Local_Time_Type):]
+ timezone_string_table := buffer[:real_hdr.charcnt]
+ buffer = buffer[real_hdr.charcnt:]
+
+ leapsecond_records := slice.reinterpret([]Leapsecond_Record, buffer[:int(real_hdr.leapcnt)*size_of(Leapsecond_Record)])
+ if len(leapsecond_records) > 0 {
+ if leapsecond_records[0].occur < 0 {
+ return
+ }
+ }
+ buffer = buffer[(int(real_hdr.leapcnt) * size_of(Leapsecond_Record)):]
+
+ standard_wall_tags := buffer[:int(real_hdr.isstdcnt)]
+ buffer = buffer[int(real_hdr.isstdcnt):]
+
+ ut_tags := buffer[:int(real_hdr.isutcnt)]
+
+ for stdwall_tag, idx in standard_wall_tags {
+ ut_tag := ut_tags[idx]
+
+ if (stdwall_tag != 0 && stdwall_tag != 1) {
+ return
+ }
+ if (ut_tag != 0 && ut_tag != 1) {
+ return
+ }
+
+ if ut_tag == 1 && stdwall_tag != 1 {
+ return
+ }
+ }
+ buffer = buffer[int(real_hdr.isutcnt):]
+
+ // Start of footer
+ if buffer[0] != '\n' {
+ return
+ }
+ buffer = buffer[1:]
+
+ if buffer[0] == ':' {
+ return
+ }
+
+ end_idx := 0
+ for ch in buffer {
+ if ch == '\n' {
+ break
+ }
+
+ if ch == 0 {
+ return
+ }
+ end_idx += 1
+ }
+ footer_str := string(buffer[:end_idx])
+
+ // UTC is a special case, we don't need to alloc
+ if len(local_time_types) == 1 {
+ name := cstring(raw_data(timezone_string_table[local_time_types[0].idx:]))
+ if name != "UTC" {
+ return
+ }
+
+ return nil, true
+ }
+
+ ltt_names, err := make([dynamic]string, 0, len(local_time_types), allocator)
+ if err != nil { return }
+ defer if err != nil {
+ for name in ltt_names {
+ delete(name, allocator)
+ }
+ delete(ltt_names)
+ }
+
+ for ltt in local_time_types {
+ name := cstring(raw_data(timezone_string_table[ltt.idx:]))
+ ltt_name: string
+
+ ltt_name, err = strings.clone_from_cstring_bounded(name, len(timezone_string_table), allocator)
+ if err != nil { return }
+
+ append(&ltt_names, ltt_name)
+ }
+
+ records: []datetime.TZ_Record
+ records, err = make([]datetime.TZ_Record, len(transition_times), allocator)
+ if err != nil { return }
+ defer if err != nil { delete(records, allocator) }
+
+ for trans_time, idx in transition_times {
+ trans_idx := transition_types[idx]
+ ltt := local_time_types[trans_idx]
+
+ records[idx] = datetime.TZ_Record{
+ time = i64(trans_time),
+ utc_offset = i64(ltt.utoff),
+ shortname = ltt_names[trans_idx],
+ dst = bool(ltt.dst),
+ }
+ }
+
+ rrule, ok2 := parse_posix_tz(footer_str, allocator)
+ if !ok2 { return }
+ defer if err != nil {
+ delete(rrule.std_name, allocator)
+ delete(rrule.dst_name, allocator)
+ }
+
+ region_name_out: string
+ region_name_out, err = strings.clone(region_name, allocator)
+ if err != nil { return }
+ defer if err != nil { delete(region_name_out, allocator) }
+
+ region: ^datetime.TZ_Region
+ region, err = new_clone(datetime.TZ_Region{
+ records = records,
+ shortnames = ltt_names[:],
+ name = region_name_out,
+ rrule = rrule,
+ }, allocator)
+ if err != nil {
+ return
+ }
+
+ return region, true
+}
diff --git a/core/unicode/utf16/utf16.odin b/core/unicode/utf16/utf16.odin
index 6bdd6558a..e2bcf7f68 100644
--- a/core/unicode/utf16/utf16.odin
+++ b/core/unicode/utf16/utf16.odin
@@ -106,6 +106,18 @@ decode :: proc(d: []rune, s: []u16) -> (n: int) {
return
}
+rune_count :: proc(s: []u16) -> (n: int) {
+ for i := 0; i < len(s); i += 1 {
+ c := s[i]
+ if _surr1 <= c && c < _surr2 && i+1 < len(s) &&
+ _surr2 <= s[i+1] && s[i+1] < _surr3 {
+ i += 1
+ }
+ n += 1
+ }
+ return
+}
+
decode_to_utf8 :: proc(d: []byte, s: []u16) -> (n: int) {
for i := 0; i < len(s); i += 1 {
@@ -127,4 +139,4 @@ decode_to_utf8 :: proc(d: []byte, s: []u16) -> (n: int) {
n += copy(d[n:], b[:w])
}
return
-} \ No newline at end of file
+}
diff --git a/core/unicode/utf8/utf8string/string.odin b/core/unicode/utf8/utf8string/string.odin
index 431939efe..4b0fe7241 100644
--- a/core/unicode/utf8/utf8string/string.odin
+++ b/core/unicode/utf8/utf8string/string.odin
@@ -66,7 +66,7 @@ at :: proc(s: ^String, i: int, loc := #caller_location) -> (r: rune) {
return
case s.rune_count-1:
- r, s.width = utf8.decode_rune_in_string(s.contents)
+ r, s.width = utf8.decode_last_rune(s.contents)
s.rune_pos = i
s.byte_pos = _len(s.contents) - s.width
return