mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-01 12:18:15 +00:00
Merge remote-tracking branch 'offical/master'
This commit is contained in:
@@ -145,6 +145,11 @@ initialize_symbols :: proc(
|
||||
}
|
||||
}
|
||||
|
||||
// No field for it in the struct.
|
||||
if handle == nil {
|
||||
handle = load_library(library_path) or_return
|
||||
}
|
||||
|
||||
// Buffer to concatenate the prefix + symbol name.
|
||||
prefixed_symbol_buf: [2048]u8 = ---
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ parse_or_exit :: proc(
|
||||
args = program_args[1:]
|
||||
}
|
||||
|
||||
error := parse(model, args, style)
|
||||
error := parse(model, args, style, true, true, allocator, loc)
|
||||
if error != nil {
|
||||
stderr := os.stream_from_handle(os.stderr)
|
||||
|
||||
|
||||
+9
-1
@@ -613,6 +613,10 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any, flush := true, newline :
|
||||
i += 1
|
||||
width_index, _, index_ok := _arg_number(fmt, &i, len(args))
|
||||
|
||||
if !index_ok {
|
||||
width_index, index_ok = error_check_arg(fi, false, unused_args^)
|
||||
}
|
||||
|
||||
if index_ok {
|
||||
unused_args^ -= {width_index}
|
||||
|
||||
@@ -638,6 +642,10 @@ wprintf :: proc(w: io.Writer, fmt: string, args: ..any, flush := true, newline :
|
||||
i += 1
|
||||
precision_index, _, index_ok := _arg_number(fmt, &i, len(args))
|
||||
|
||||
if !index_ok {
|
||||
precision_index, index_ok = error_check_arg(fi, false, unused_args^)
|
||||
}
|
||||
|
||||
if index_ok {
|
||||
unused_args^ -= {precision_index}
|
||||
fi.prec, _, fi.prec_set = int_from_arg(args, precision_index)
|
||||
@@ -1289,7 +1297,7 @@ fmt_rune :: proc(fi: ^Info, r: rune, verb: rune) {
|
||||
case 'q', 'w':
|
||||
fi.n += io.write_quoted_rune(fi.writer, r)
|
||||
case:
|
||||
fmt_int(fi, u64(r), false, 32, verb)
|
||||
fmt_int(fi, u64(u32(r)), false, 32, verb)
|
||||
}
|
||||
}
|
||||
// Formats an integer value according to the specified formatting verb.
|
||||
|
||||
@@ -19,7 +19,6 @@ default_random_generator :: runtime.default_random_generator
|
||||
@(require_results)
|
||||
create :: proc(seed: u64) -> (state: Default_Random_State) {
|
||||
seed := seed
|
||||
runtime.default_random_generator(&state)
|
||||
runtime.default_random_generator_proc(&state, .Reset, ([^]byte)(&seed)[:size_of(seed)])
|
||||
return
|
||||
}
|
||||
|
||||
+2
-2
@@ -16,8 +16,8 @@ a multipointer can be indexed, but does not have a definite length. A slice is
|
||||
a pointer that points to multiple objects equipped with the length, specifying
|
||||
the amount of objects a slice points to.
|
||||
|
||||
When object's values are read through a pointer, that operation is called a
|
||||
*load* operation. When memory is read through a pointer, that operation is
|
||||
When an object's values are read through a pointer, that operation is called a
|
||||
*load* operation. When memory is written to through a pointer, that operation is
|
||||
called a *store* operation. Both of these operations can be called a *memory
|
||||
access operation*.
|
||||
|
||||
|
||||
@@ -341,7 +341,9 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
new_end := start + size
|
||||
if start < old_end && old_end == block.used && new_end <= block.reserved {
|
||||
// grow data in-place, adjusting next allocation
|
||||
prev_used := block.used
|
||||
_ = alloc_from_memory_block(block, new_end - old_end, 1, default_commit_size=arena.default_commit_size) or_return
|
||||
arena.total_used += block.used - prev_used
|
||||
data = block.base[start:new_end]
|
||||
return
|
||||
}
|
||||
|
||||
+21
-9
@@ -7,10 +7,11 @@ package net
|
||||
*/
|
||||
|
||||
/*
|
||||
Copyright 2022 Tetralux <tetraluxonpc@gmail.com>
|
||||
Copyright 2022 Colin Davidson <colrdavidson@gmail.com>
|
||||
Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Copyright 2024 Feoramund <rune@swevencraft.org>.
|
||||
Copyright 2022 Tetralux <tetraluxonpc@gmail.com>
|
||||
Copyright 2022 Colin Davidson <colrdavidson@gmail.com>
|
||||
Copyright 2022 Jeroen van Rijn <nom@duclavier.com>.
|
||||
Copyright 2024 Feoramund <rune@swevencraft.org>.
|
||||
Copyright 2025 Christiano Haesbaert <haesbaert@haesbaert.org>.
|
||||
Made available under Odin's BSD-3 license.
|
||||
|
||||
List of contributors:
|
||||
@@ -18,12 +19,14 @@ package net
|
||||
Colin Davidson: Linux platform code, OSX platform code, Odin-native DNS resolver
|
||||
Jeroen van Rijn: Cross platform unification, code style, documentation
|
||||
Feoramund: FreeBSD platform code
|
||||
Haesbaert: Security fixes
|
||||
*/
|
||||
|
||||
import "core:mem"
|
||||
import "core:strings"
|
||||
import "core:time"
|
||||
import "core:os"
|
||||
import "core:math/rand"
|
||||
/*
|
||||
Default configuration for DNS resolution.
|
||||
*/
|
||||
@@ -227,7 +230,7 @@ get_dns_records_from_nameservers :: proc(hostname: string, type: DNS_Record_Type
|
||||
}
|
||||
|
||||
hdr := DNS_Header{
|
||||
id = 0,
|
||||
id = u16be(rand.uint32()),
|
||||
is_response = false,
|
||||
opcode = 0,
|
||||
is_authoritative = false,
|
||||
@@ -272,17 +275,23 @@ get_dns_records_from_nameservers :: proc(hostname: string, type: DNS_Record_Type
|
||||
return nil, .Connection_Error
|
||||
}
|
||||
|
||||
recv_sz, _ := recv_udp(conn, dns_response_buf[:]) or_continue
|
||||
recv_sz, src := recv_udp(conn, dns_response_buf[:]) or_continue
|
||||
if recv_sz == 0 {
|
||||
continue
|
||||
}
|
||||
if src != name_server {
|
||||
continue
|
||||
}
|
||||
|
||||
dns_response = dns_response_buf[:recv_sz]
|
||||
|
||||
rsp, _ok := parse_response(dns_response, type)
|
||||
rsp, xid, _ok := parse_response(dns_response, type)
|
||||
if !_ok {
|
||||
return nil, .Server_Error
|
||||
}
|
||||
if id != xid {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(rsp) == 0 {
|
||||
continue
|
||||
@@ -803,7 +812,7 @@ parse_record :: proc(packet: []u8, cur_off: ^int, filter: DNS_Record_Type = nil)
|
||||
- Data[]
|
||||
*/
|
||||
|
||||
parse_response :: proc(response: []u8, filter: DNS_Record_Type = nil, allocator := context.allocator) -> (records: []DNS_Record, ok: bool) {
|
||||
parse_response :: proc(response: []u8, filter: DNS_Record_Type = nil, allocator := context.allocator) -> (records: []DNS_Record, xid: u16be, ok: bool) {
|
||||
context.allocator = allocator
|
||||
|
||||
HEADER_SIZE_BYTES :: 12
|
||||
@@ -816,11 +825,13 @@ parse_response :: proc(response: []u8, filter: DNS_Record_Type = nil, allocator
|
||||
dns_hdr_chunks := mem.slice_data_cast([]u16be, response[:HEADER_SIZE_BYTES])
|
||||
hdr := unpack_dns_header(dns_hdr_chunks[0], dns_hdr_chunks[1])
|
||||
if !hdr.is_response {
|
||||
delete(_records)
|
||||
return
|
||||
}
|
||||
|
||||
question_count := int(dns_hdr_chunks[2])
|
||||
if question_count != 1 {
|
||||
delete(_records)
|
||||
return
|
||||
}
|
||||
answer_count := int(dns_hdr_chunks[3])
|
||||
@@ -872,6 +883,7 @@ parse_response :: proc(response: []u8, filter: DNS_Record_Type = nil, allocator
|
||||
append(&_records, rec)
|
||||
}
|
||||
}
|
||||
xid = hdr.id
|
||||
|
||||
return _records[:], true
|
||||
return _records[:], xid, true
|
||||
}
|
||||
|
||||
@@ -80,8 +80,9 @@ _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())
|
||||
close(socket)
|
||||
return {}, Dial_Error(win.WSAGetLastError())
|
||||
return {}, err
|
||||
}
|
||||
|
||||
if options.no_delay {
|
||||
|
||||
+118
-6
@@ -20,7 +20,7 @@ read_directory :: proc(f: ^File, n: int, allocator: runtime.Allocator) -> (files
|
||||
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
|
||||
it := read_directory_iterator_create(f) or_return
|
||||
it := read_directory_iterator_create(f)
|
||||
defer _read_directory_iterator_destroy(&it)
|
||||
|
||||
dfi := make([dynamic]File_Info, 0, size, temp_allocator())
|
||||
@@ -34,9 +34,14 @@ read_directory :: proc(f: ^File, n: int, allocator: runtime.Allocator) -> (files
|
||||
if n > 0 && index == n {
|
||||
break
|
||||
}
|
||||
|
||||
_ = read_directory_iterator_error(&it) or_break
|
||||
|
||||
append(&dfi, file_info_clone(fi, allocator) or_return)
|
||||
}
|
||||
|
||||
_ = read_directory_iterator_error(&it) or_return
|
||||
|
||||
return slice.clone(dfi[:], allocator)
|
||||
}
|
||||
|
||||
@@ -61,22 +66,129 @@ read_all_directory_by_path :: proc(path: string, allocator: runtime.Allocator) -
|
||||
|
||||
|
||||
Read_Directory_Iterator :: struct {
|
||||
f: ^File,
|
||||
f: ^File,
|
||||
err: struct {
|
||||
err: Error,
|
||||
path: [dynamic]byte,
|
||||
},
|
||||
index: int,
|
||||
impl: Read_Directory_Iterator_Impl,
|
||||
}
|
||||
|
||||
/*
|
||||
Creates a directory iterator with the given directory.
|
||||
|
||||
@(require_results)
|
||||
read_directory_iterator_create :: proc(f: ^File) -> (Read_Directory_Iterator, Error) {
|
||||
return _read_directory_iterator_create(f)
|
||||
For an example on how to use the iterator, see `read_directory_iterator`.
|
||||
*/
|
||||
read_directory_iterator_create :: proc(f: ^File) -> (it: Read_Directory_Iterator) {
|
||||
read_directory_iterator_init(&it, f)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Initialize a directory iterator with the given directory.
|
||||
|
||||
This procedure may be called on an existing iterator to reuse it for another directory.
|
||||
|
||||
For an example on how to use the iterator, see `read_directory_iterator`.
|
||||
*/
|
||||
read_directory_iterator_init :: proc(it: ^Read_Directory_Iterator, f: ^File) {
|
||||
it.err.err = nil
|
||||
it.err.path.allocator = file_allocator()
|
||||
clear(&it.err.path)
|
||||
|
||||
it.f = f
|
||||
it.index = 0
|
||||
|
||||
_read_directory_iterator_init(it, f)
|
||||
}
|
||||
|
||||
/*
|
||||
Destroys a directory iterator.
|
||||
*/
|
||||
read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
|
||||
if it == nil {
|
||||
return
|
||||
}
|
||||
|
||||
delete(it.err.path)
|
||||
|
||||
_read_directory_iterator_destroy(it)
|
||||
}
|
||||
|
||||
// NOTE(bill): `File_Info` does not need to deleted on each iteration. Any copies must be manually copied with `file_info_clone`
|
||||
/*
|
||||
Retrieve the last error that happened during iteration.
|
||||
*/
|
||||
@(require_results)
|
||||
read_directory_iterator_error :: proc(it: ^Read_Directory_Iterator) -> (path: string, err: Error) {
|
||||
return string(it.err.path[:]), it.err.err
|
||||
}
|
||||
|
||||
@(private)
|
||||
read_directory_iterator_set_error :: proc(it: ^Read_Directory_Iterator, path: string, err: Error) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
resize(&it.err.path, len(path))
|
||||
copy(it.err.path[:], path)
|
||||
|
||||
it.err.err = err
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the next file info entry for the iterator's directory.
|
||||
|
||||
The given `File_Info` is reused in subsequent calls so a copy (`file_info_clone`) has to be made to
|
||||
extend its lifetime.
|
||||
|
||||
Example:
|
||||
package main
|
||||
|
||||
import "core:fmt"
|
||||
import os "core:os/os2"
|
||||
|
||||
main :: proc() {
|
||||
f, oerr := os.open("core")
|
||||
ensure(oerr == nil)
|
||||
defer os.close(f)
|
||||
|
||||
it := os.read_directory_iterator_create(f)
|
||||
defer os.read_directory_iterator_destroy(&it)
|
||||
|
||||
for info in os.read_directory_iterator(&it) {
|
||||
// Optionally break on the first error:
|
||||
// Supports not doing this, and keeping it going with remaining items.
|
||||
// _ = os.read_directory_iterator_error(&it) or_break
|
||||
|
||||
// Handle error as we go:
|
||||
// Again, no need to do this as it will keep going with remaining items.
|
||||
if path, err := os.read_directory_iterator_error(&it); err != nil {
|
||||
fmt.eprintfln("failed reading %s: %s", path, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Or, do not handle errors during iteration, and just check the error at the end.
|
||||
|
||||
|
||||
fmt.printfln("%#v", info)
|
||||
}
|
||||
|
||||
// Handle error if one happened during iteration at the end:
|
||||
if path, err := os.read_directory_iterator_error(&it); err != nil {
|
||||
fmt.eprintfln("read directory failed at %s: %s", path, err)
|
||||
}
|
||||
}
|
||||
*/
|
||||
@(require_results)
|
||||
read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
|
||||
if it.f == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if it.index == 0 && it.err.err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return _read_directory_iterator(it)
|
||||
}
|
||||
|
||||
+32
-14
@@ -8,12 +8,11 @@ Read_Directory_Iterator_Impl :: struct {
|
||||
dirent_backing: []u8,
|
||||
dirent_buflen: int,
|
||||
dirent_off: int,
|
||||
index: int,
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
_read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
|
||||
scan_entries :: proc(dfd: linux.Fd, entries: []u8, offset: ^int) -> (fd: linux.Fd, file_name: string) {
|
||||
scan_entries :: proc(it: ^Read_Directory_Iterator, dfd: linux.Fd, entries: []u8, offset: ^int) -> (fd: linux.Fd, file_name: string) {
|
||||
for d in linux.dirent_iterate_buf(entries, offset) {
|
||||
file_name = linux.dirent_name(d)
|
||||
if file_name == "." || file_name == ".." {
|
||||
@@ -24,18 +23,21 @@ _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info
|
||||
entry_fd, errno := linux.openat(dfd, file_name_cstr, {.NOFOLLOW, .PATH})
|
||||
if errno == .NONE {
|
||||
return entry_fd, file_name
|
||||
} else {
|
||||
read_directory_iterator_set_error(it, file_name, _get_platform_error(errno))
|
||||
}
|
||||
}
|
||||
|
||||
return -1, ""
|
||||
}
|
||||
|
||||
index = it.impl.index
|
||||
it.impl.index += 1
|
||||
index = it.index
|
||||
it.index += 1
|
||||
|
||||
dfd := linux.Fd(_fd(it.f))
|
||||
|
||||
entries := it.impl.dirent_backing[:it.impl.dirent_buflen]
|
||||
entry_fd, file_name := scan_entries(dfd, entries, &it.impl.dirent_off)
|
||||
entry_fd, file_name := scan_entries(it, dfd, entries, &it.impl.dirent_off)
|
||||
|
||||
for entry_fd == -1 {
|
||||
if len(it.impl.dirent_backing) == 0 {
|
||||
@@ -58,44 +60,60 @@ _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info
|
||||
it.impl.dirent_buflen = buflen
|
||||
entries = it.impl.dirent_backing[:buflen]
|
||||
break loop
|
||||
case: // error
|
||||
case:
|
||||
read_directory_iterator_set_error(it, name(it.f), _get_platform_error(errno))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
entry_fd, file_name = scan_entries(dfd, entries, &it.impl.dirent_off)
|
||||
entry_fd, file_name = scan_entries(it, dfd, entries, &it.impl.dirent_off)
|
||||
}
|
||||
defer linux.close(entry_fd)
|
||||
|
||||
// PERF: reuse the fullpath string like on posix and wasi.
|
||||
file_info_delete(it.impl.prev_fi, file_allocator())
|
||||
fi, _ = _fstat_internal(entry_fd, file_allocator())
|
||||
|
||||
err: Error
|
||||
fi, err = _fstat_internal(entry_fd, file_allocator())
|
||||
it.impl.prev_fi = fi
|
||||
|
||||
if err != nil {
|
||||
path, _ := _get_full_path(entry_fd, temp_allocator())
|
||||
read_directory_iterator_set_error(it, path, err)
|
||||
}
|
||||
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
_read_directory_iterator_create :: proc(f: ^File) -> (Read_Directory_Iterator, Error) {
|
||||
_read_directory_iterator_init :: proc(it: ^Read_Directory_Iterator, f: ^File) {
|
||||
// NOTE: Allow calling `init` to target a new directory with the same iterator.
|
||||
it.impl.dirent_buflen = 0
|
||||
it.impl.dirent_off = 0
|
||||
|
||||
if f == nil || f.impl == nil {
|
||||
return {}, .Invalid_File
|
||||
read_directory_iterator_set_error(it, "", .Invalid_File)
|
||||
return
|
||||
}
|
||||
|
||||
stat: linux.Stat
|
||||
errno := linux.fstat(linux.Fd(fd(f)), &stat)
|
||||
if errno != .NONE {
|
||||
return {}, _get_platform_error(errno)
|
||||
read_directory_iterator_set_error(it, name(f), _get_platform_error(errno))
|
||||
return
|
||||
}
|
||||
|
||||
if (stat.mode & linux.S_IFMT) != linux.S_IFDIR {
|
||||
return {}, .Invalid_Dir
|
||||
read_directory_iterator_set_error(it, name(f), .Invalid_Dir)
|
||||
return
|
||||
}
|
||||
return {f = f}, nil
|
||||
}
|
||||
|
||||
_read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
|
||||
if it == nil {
|
||||
return
|
||||
}
|
||||
|
||||
delete(it.impl.dirent_backing, file_allocator())
|
||||
file_info_delete(it.impl.prev_fi, file_allocator())
|
||||
}
|
||||
|
||||
+37
-28
@@ -6,7 +6,6 @@ import "core:sys/posix"
|
||||
|
||||
Read_Directory_Iterator_Impl :: struct {
|
||||
dir: posix.DIR,
|
||||
idx: int,
|
||||
fullpath: [dynamic]byte,
|
||||
}
|
||||
|
||||
@@ -14,14 +13,16 @@ Read_Directory_Iterator_Impl :: struct {
|
||||
_read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
|
||||
fimpl := (^File_Impl)(it.f.impl)
|
||||
|
||||
index = it.impl.idx
|
||||
it.impl.idx += 1
|
||||
index = it.index
|
||||
it.index += 1
|
||||
|
||||
for {
|
||||
posix.set_errno(nil)
|
||||
entry := posix.readdir(it.impl.dir)
|
||||
if entry == nil {
|
||||
// NOTE(laytan): would be good to have an `error` field on the `Read_Directory_Iterator`
|
||||
// There isn't a way to now know if it failed or if we are at the end.
|
||||
if errno := posix.errno(); errno != nil {
|
||||
read_directory_iterator_set_error(it, name(it.f), _get_platform_error(errno))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -31,54 +32,62 @@ _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info
|
||||
}
|
||||
sname := string(cname)
|
||||
|
||||
stat: posix.stat_t
|
||||
if posix.fstatat(posix.dirfd(it.impl.dir), cname, &stat, { .SYMLINK_NOFOLLOW }) != .OK {
|
||||
// NOTE(laytan): would be good to have an `error` field on the `Read_Directory_Iterator`
|
||||
// There isn't a way to now know if it failed or if we are at the end.
|
||||
return
|
||||
}
|
||||
|
||||
n := len(fimpl.name)+1
|
||||
if err := non_zero_resize(&it.impl.fullpath, n+len(sname)); err != nil {
|
||||
// Can't really tell caller we had an error, sad.
|
||||
read_directory_iterator_set_error(it, sname, err)
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
copy(it.impl.fullpath[n:], sname)
|
||||
|
||||
stat: posix.stat_t
|
||||
if posix.fstatat(posix.dirfd(it.impl.dir), cname, &stat, { .SYMLINK_NOFOLLOW }) != .OK {
|
||||
read_directory_iterator_set_error(it, string(it.impl.fullpath[:]), _get_platform_error())
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
fi = internal_stat(stat, string(it.impl.fullpath[:]))
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
_read_directory_iterator_create :: proc(f: ^File) -> (iter: Read_Directory_Iterator, err: Error) {
|
||||
_read_directory_iterator_init :: proc(it: ^Read_Directory_Iterator, f: ^File) {
|
||||
if f == nil || f.impl == nil {
|
||||
err = .Invalid_File
|
||||
read_directory_iterator_set_error(it, "", .Invalid_File)
|
||||
return
|
||||
}
|
||||
|
||||
impl := (^File_Impl)(f.impl)
|
||||
|
||||
iter.f = f
|
||||
iter.impl.idx = 0
|
||||
// NOTE: Allow calling `init` to target a new directory with the same iterator.
|
||||
it.impl.fullpath.allocator = file_allocator()
|
||||
clear(&it.impl.fullpath)
|
||||
if err := reserve(&it.impl.fullpath, len(impl.name)+128); err != nil {
|
||||
read_directory_iterator_set_error(it, name(f), err)
|
||||
return
|
||||
}
|
||||
|
||||
iter.impl.fullpath = make([dynamic]byte, 0, len(impl.name)+128, file_allocator()) or_return
|
||||
append(&iter.impl.fullpath, impl.name)
|
||||
append(&iter.impl.fullpath, "/")
|
||||
defer if err != nil { delete(iter.impl.fullpath) }
|
||||
append(&it.impl.fullpath, impl.name)
|
||||
append(&it.impl.fullpath, "/")
|
||||
|
||||
// `fdopendir` consumes the file descriptor so we need to `dup` it.
|
||||
dupfd := posix.dup(impl.fd)
|
||||
if dupfd == -1 {
|
||||
err = _get_platform_error()
|
||||
read_directory_iterator_set_error(it, name(f), _get_platform_error())
|
||||
return
|
||||
}
|
||||
defer if err != nil { posix.close(dupfd) }
|
||||
defer if it.err.err != nil { posix.close(dupfd) }
|
||||
|
||||
iter.impl.dir = posix.fdopendir(dupfd)
|
||||
if iter.impl.dir == nil {
|
||||
err = _get_platform_error()
|
||||
// NOTE: Allow calling `init` to target a new directory with the same iterator.
|
||||
if it.impl.dir != nil {
|
||||
posix.closedir(it.impl.dir)
|
||||
}
|
||||
|
||||
it.impl.dir = posix.fdopendir(dupfd)
|
||||
if it.impl.dir == nil {
|
||||
read_directory_iterator_set_error(it, name(f), _get_platform_error())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -86,7 +95,7 @@ _read_directory_iterator_create :: proc(f: ^File) -> (iter: Read_Directory_Itera
|
||||
}
|
||||
|
||||
_read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
|
||||
if it == nil || it.impl.dir == nil {
|
||||
if it.impl.dir == nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,230 @@
|
||||
package os2
|
||||
|
||||
import "core:container/queue"
|
||||
|
||||
/*
|
||||
A recursive directory walker.
|
||||
|
||||
Note that none of the fields should be accessed directly.
|
||||
*/
|
||||
Walker :: struct {
|
||||
todo: queue.Queue(string),
|
||||
skip_dir: bool,
|
||||
err: struct {
|
||||
path: [dynamic]byte,
|
||||
err: Error,
|
||||
},
|
||||
iter: Read_Directory_Iterator,
|
||||
}
|
||||
|
||||
walker_init_path :: proc(w: ^Walker, path: string) {
|
||||
cloned_path, err := clone_string(path, file_allocator())
|
||||
if err != nil {
|
||||
walker_set_error(w, path, err)
|
||||
return
|
||||
}
|
||||
|
||||
walker_clear(w)
|
||||
|
||||
if _, err = queue.push(&w.todo, cloned_path); err != nil {
|
||||
walker_set_error(w, cloned_path, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
walker_init_file :: proc(w: ^Walker, f: ^File) {
|
||||
handle, err := clone(f)
|
||||
if err != nil {
|
||||
path, _ := clone_string(name(f), file_allocator())
|
||||
walker_set_error(w, path, err)
|
||||
return
|
||||
}
|
||||
|
||||
walker_clear(w)
|
||||
|
||||
read_directory_iterator_init(&w.iter, handle)
|
||||
}
|
||||
|
||||
/*
|
||||
Initializes a walker, either using a path or a file pointer to a directory the walker will start at.
|
||||
|
||||
You are allowed to repeatedly call this to reuse it for later walks.
|
||||
|
||||
For an example on how to use the walker, see `walker_walk`.
|
||||
*/
|
||||
walker_init :: proc {
|
||||
walker_init_path,
|
||||
walker_init_file,
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
walker_create_path :: proc(path: string) -> (w: Walker) {
|
||||
walker_init_path(&w, path)
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
walker_create_file :: proc(f: ^File) -> (w: Walker) {
|
||||
walker_init_file(&w, f)
|
||||
return
|
||||
}
|
||||
|
||||
/*
|
||||
Creates a walker, either using a path or a file pointer to a directory the walker will start at.
|
||||
|
||||
For an example on how to use the walker, see `walker_walk`.
|
||||
*/
|
||||
walker_create :: proc {
|
||||
walker_create_path,
|
||||
walker_create_file,
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the last error that occurred during the walker's operations.
|
||||
|
||||
Can be called while iterating, or only at the end to check if anything failed.
|
||||
*/
|
||||
@(require_results)
|
||||
walker_error :: proc(w: ^Walker) -> (path: string, err: Error) {
|
||||
return string(w.err.path[:]), w.err.err
|
||||
}
|
||||
|
||||
@(private)
|
||||
walker_set_error :: proc(w: ^Walker, path: string, err: Error) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
resize(&w.err.path, len(path))
|
||||
copy(w.err.path[:], path)
|
||||
|
||||
w.err.err = err
|
||||
}
|
||||
|
||||
@(private)
|
||||
walker_clear :: proc(w: ^Walker) {
|
||||
w.iter.f = nil
|
||||
w.skip_dir = false
|
||||
|
||||
w.err.path.allocator = file_allocator()
|
||||
clear(&w.err.path)
|
||||
|
||||
w.todo.data.allocator = file_allocator()
|
||||
for path in queue.pop_front_safe(&w.todo) {
|
||||
delete(path, file_allocator())
|
||||
}
|
||||
}
|
||||
|
||||
walker_destroy :: proc(w: ^Walker) {
|
||||
walker_clear(w)
|
||||
queue.destroy(&w.todo)
|
||||
delete(w.err.path)
|
||||
read_directory_iterator_destroy(&w.iter)
|
||||
}
|
||||
|
||||
// Marks the current directory to be skipped (not entered into).
|
||||
walker_skip_dir :: proc(w: ^Walker) {
|
||||
w.skip_dir = true
|
||||
}
|
||||
|
||||
/*
|
||||
Returns the next file info in the iterator, files are iterated in breadth-first order.
|
||||
|
||||
If an error occurred opening a directory, you may get zero'd info struct and
|
||||
`walker_error` will return the error.
|
||||
|
||||
Example:
|
||||
package main
|
||||
|
||||
import "core:fmt"
|
||||
import "core:strings"
|
||||
import os "core:os/os2"
|
||||
|
||||
main :: proc() {
|
||||
w := os.walker_create("core")
|
||||
defer os.walker_destroy(&w)
|
||||
|
||||
for info in os.walker_walk(&w) {
|
||||
// Optionally break on the first error:
|
||||
// _ = walker_error(&w) or_break
|
||||
|
||||
// Or, handle error as we go:
|
||||
if path, err := os.walker_error(&w); err != nil {
|
||||
fmt.eprintfln("failed walking %s: %s", path, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Or, do not handle errors during iteration, and just check the error at the end.
|
||||
|
||||
|
||||
|
||||
// Skip a directory:
|
||||
if strings.has_suffix(info.fullpath, ".git") {
|
||||
os.walker_skip_dir(&w)
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.printfln("%#v", info)
|
||||
}
|
||||
|
||||
// Handle error if one happened during iteration at the end:
|
||||
if path, err := os.walker_error(&w); err != nil {
|
||||
fmt.eprintfln("failed walking %s: %v", path, err)
|
||||
}
|
||||
}
|
||||
*/
|
||||
@(require_results)
|
||||
walker_walk :: proc(w: ^Walker) -> (fi: File_Info, ok: bool) {
|
||||
if w.skip_dir {
|
||||
w.skip_dir = false
|
||||
if skip, sok := queue.pop_back_safe(&w.todo); sok {
|
||||
delete(skip, file_allocator())
|
||||
}
|
||||
}
|
||||
|
||||
if w.iter.f == nil {
|
||||
if queue.len(w.todo) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
next := queue.pop_front(&w.todo)
|
||||
|
||||
handle, err := open(next)
|
||||
if err != nil {
|
||||
walker_set_error(w, next, err)
|
||||
return {}, true
|
||||
}
|
||||
|
||||
read_directory_iterator_init(&w.iter, handle)
|
||||
|
||||
delete(next, file_allocator())
|
||||
}
|
||||
|
||||
info, _, iter_ok := read_directory_iterator(&w.iter)
|
||||
|
||||
if path, err := read_directory_iterator_error(&w.iter); err != nil {
|
||||
walker_set_error(w, path, err)
|
||||
}
|
||||
|
||||
if !iter_ok {
|
||||
close(w.iter.f)
|
||||
w.iter.f = nil
|
||||
return walker_walk(w)
|
||||
}
|
||||
|
||||
if info.type == .Directory {
|
||||
path, err := clone_string(info.fullpath, file_allocator())
|
||||
if err != nil {
|
||||
walker_set_error(w, "", err)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = queue.push_back(&w.todo, path)
|
||||
if err != nil {
|
||||
walker_set_error(w, path, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return info, iter_ok
|
||||
}
|
||||
+37
-23
@@ -1,6 +1,8 @@
|
||||
#+private
|
||||
package os2
|
||||
|
||||
import "base:runtime"
|
||||
import "core:slice"
|
||||
import "base:intrinsics"
|
||||
import "core:sys/wasm/wasi"
|
||||
|
||||
@@ -8,7 +10,6 @@ Read_Directory_Iterator_Impl :: struct {
|
||||
fullpath: [dynamic]byte,
|
||||
buf: []byte,
|
||||
off: int,
|
||||
idx: int,
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
@@ -17,8 +18,8 @@ _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info
|
||||
|
||||
buf := it.impl.buf[it.impl.off:]
|
||||
|
||||
index = it.impl.idx
|
||||
it.impl.idx += 1
|
||||
index = it.index
|
||||
it.index += 1
|
||||
|
||||
for {
|
||||
if len(buf) < size_of(wasi.dirent_t) {
|
||||
@@ -28,10 +29,7 @@ _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info
|
||||
entry := intrinsics.unaligned_load((^wasi.dirent_t)(raw_data(buf)))
|
||||
buf = buf[size_of(wasi.dirent_t):]
|
||||
|
||||
if len(buf) < int(entry.d_namlen) {
|
||||
// shouldn't be possible.
|
||||
return
|
||||
}
|
||||
assert(len(buf) < int(entry.d_namlen))
|
||||
|
||||
name := string(buf[:entry.d_namlen])
|
||||
buf = buf[entry.d_namlen:]
|
||||
@@ -43,7 +41,8 @@ _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info
|
||||
|
||||
n := len(fimpl.name)+1
|
||||
if alloc_err := non_zero_resize(&it.impl.fullpath, n+len(name)); alloc_err != nil {
|
||||
// Can't really tell caller we had an error, sad.
|
||||
read_directory_iterator_set_error(it, name, alloc_err)
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
copy(it.impl.fullpath[n:], name)
|
||||
@@ -55,6 +54,7 @@ _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info
|
||||
ino = entry.d_ino,
|
||||
filetype = entry.d_type,
|
||||
}
|
||||
read_directory_iterator_set_error(it, string(it.impl.fullpath[:]), _get_platform_error(err))
|
||||
}
|
||||
|
||||
fi = internal_stat(stat, string(it.impl.fullpath[:]))
|
||||
@@ -63,27 +63,35 @@ _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info
|
||||
}
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
_read_directory_iterator_create :: proc(f: ^File) -> (iter: Read_Directory_Iterator, err: Error) {
|
||||
_read_directory_iterator_init :: proc(it: ^Read_Directory_Iterator, f: ^File) {
|
||||
// NOTE: Allow calling `init` to target a new directory with the same iterator.
|
||||
it.impl.off = 0
|
||||
|
||||
if f == nil || f.impl == nil {
|
||||
err = .Invalid_File
|
||||
read_directory_iterator_set_error(it, "", .Invalid_File)
|
||||
return
|
||||
}
|
||||
|
||||
impl := (^File_Impl)(f.impl)
|
||||
iter.f = f
|
||||
|
||||
buf: [dynamic]byte
|
||||
// NOTE: Allow calling `init` to target a new directory with the same iterator.
|
||||
if it.impl.buf != nil {
|
||||
buf = slice.into_dynamic(it.impl.buf)
|
||||
}
|
||||
buf.allocator = file_allocator()
|
||||
defer if err != nil { delete(buf) }
|
||||
|
||||
// NOTE: this is very grug.
|
||||
defer if it.err.err != nil { delete(buf) }
|
||||
|
||||
for {
|
||||
non_zero_resize(&buf, 512 if len(buf) == 0 else len(buf)*2) or_return
|
||||
if err := non_zero_resize(&buf, 512 if len(buf) == 0 else len(buf)*2); err != nil {
|
||||
read_directory_iterator_set_error(it, name(f), err)
|
||||
return
|
||||
}
|
||||
|
||||
n, _err := wasi.fd_readdir(__fd(f), buf[:], 0)
|
||||
if _err != nil {
|
||||
err = _get_platform_error(_err)
|
||||
n, err := wasi.fd_readdir(__fd(f), buf[:], 0)
|
||||
if err != nil {
|
||||
read_directory_iterator_set_error(it, name(f), _get_platform_error(err))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -94,11 +102,18 @@ _read_directory_iterator_create :: proc(f: ^File) -> (iter: Read_Directory_Itera
|
||||
|
||||
assert(n == len(buf))
|
||||
}
|
||||
iter.impl.buf = buf[:]
|
||||
it.impl.buf = buf[:]
|
||||
|
||||
iter.impl.fullpath = make([dynamic]byte, 0, len(impl.name)+128, file_allocator()) or_return
|
||||
append(&iter.impl.fullpath, impl.name)
|
||||
append(&iter.impl.fullpath, "/")
|
||||
// NOTE: Allow calling `init` to target a new directory with the same iterator.
|
||||
it.impl.fullpath.allocator = file_allocator()
|
||||
clear(&it.impl.fullpath)
|
||||
if err := reserve(&it.impl.fullpath, len(impl.name)+128); err != nil {
|
||||
read_directory_iterator_set_error(it, name(f), err)
|
||||
return
|
||||
}
|
||||
|
||||
append(&it.impl.fullpath, impl.name)
|
||||
append(&it.impl.fullpath, "/")
|
||||
|
||||
return
|
||||
}
|
||||
@@ -106,5 +121,4 @@ _read_directory_iterator_create :: proc(f: ^File) -> (iter: Read_Directory_Itera
|
||||
_read_directory_iterator_destroy :: proc(it: ^Read_Directory_Iterator) {
|
||||
delete(it.impl.buf, file_allocator())
|
||||
delete(it.impl.fullpath)
|
||||
it^ = {}
|
||||
}
|
||||
|
||||
@@ -44,16 +44,11 @@ Read_Directory_Iterator_Impl :: struct {
|
||||
path: string,
|
||||
prev_fi: File_Info,
|
||||
no_more_files: bool,
|
||||
index: int,
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
_read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info, index: int, ok: bool) {
|
||||
if it.f == nil {
|
||||
return
|
||||
}
|
||||
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
|
||||
for !it.impl.no_more_files {
|
||||
@@ -63,19 +58,21 @@ _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info
|
||||
|
||||
fi, err = find_data_to_file_info(it.impl.path, &it.impl.find_data, file_allocator())
|
||||
if err != nil {
|
||||
read_directory_iterator_set_error(it, it.impl.path, err)
|
||||
return
|
||||
}
|
||||
|
||||
if fi.name != "" {
|
||||
it.impl.prev_fi = fi
|
||||
ok = true
|
||||
index = it.impl.index
|
||||
it.impl.index += 1
|
||||
index = it.index
|
||||
it.index += 1
|
||||
}
|
||||
|
||||
if !win32.FindNextFileW(it.impl.find_handle, &it.impl.find_data) {
|
||||
e := _get_platform_error()
|
||||
if pe, _ := is_platform_error(e); pe == i32(win32.ERROR_NO_MORE_FILES) {
|
||||
it.impl.no_more_files = true
|
||||
if pe, _ := is_platform_error(e); pe != i32(win32.ERROR_NO_MORE_FILES) {
|
||||
read_directory_iterator_set_error(it, it.impl.path, e)
|
||||
}
|
||||
it.impl.no_more_files = true
|
||||
}
|
||||
@@ -86,16 +83,27 @@ _read_directory_iterator :: proc(it: ^Read_Directory_Iterator) -> (fi: File_Info
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
_read_directory_iterator_create :: proc(f: ^File) -> (it: Read_Directory_Iterator, err: Error) {
|
||||
if f == nil {
|
||||
_read_directory_iterator_init :: proc(it: ^Read_Directory_Iterator, f: ^File) {
|
||||
it.impl.no_more_files = false
|
||||
|
||||
if f == nil || f.impl == nil {
|
||||
read_directory_iterator_set_error(it, "", .Invalid_File)
|
||||
return
|
||||
}
|
||||
|
||||
it.f = f
|
||||
impl := (^File_Impl)(f.impl)
|
||||
|
||||
// NOTE: Allow calling `init` to target a new directory with the same iterator - reset idx.
|
||||
if it.impl.find_handle != nil {
|
||||
win32.FindClose(it.impl.find_handle)
|
||||
}
|
||||
if it.impl.path != "" {
|
||||
delete(it.impl.path, file_allocator())
|
||||
}
|
||||
|
||||
if !is_directory(impl.name) {
|
||||
err = .Invalid_Dir
|
||||
read_directory_iterator_set_error(it, impl.name, .Invalid_Dir)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -118,14 +126,19 @@ _read_directory_iterator_create :: proc(f: ^File) -> (it: Read_Directory_Iterato
|
||||
|
||||
it.impl.find_handle = win32.FindFirstFileW(raw_data(wpath_search), &it.impl.find_data)
|
||||
if it.impl.find_handle == win32.INVALID_HANDLE_VALUE {
|
||||
err = _get_platform_error()
|
||||
read_directory_iterator_set_error(it, impl.name, _get_platform_error())
|
||||
return
|
||||
}
|
||||
defer if err != nil {
|
||||
defer if it.err.err != nil {
|
||||
win32.FindClose(it.impl.find_handle)
|
||||
}
|
||||
|
||||
it.impl.path = _cleanpath_from_buf(wpath, file_allocator()) or_return
|
||||
err: Error
|
||||
it.impl.path, err = _cleanpath_from_buf(wpath, file_allocator())
|
||||
if err != nil {
|
||||
read_directory_iterator_set_error(it, impl.name, err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -22,8 +22,8 @@ lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string,
|
||||
}
|
||||
|
||||
// set_env sets the value of the environment variable named by the key
|
||||
// Returns true on success, false on failure
|
||||
set_env :: proc(key, value: string) -> bool {
|
||||
// Returns Error on failure
|
||||
set_env :: proc(key, value: string) -> Error {
|
||||
return _set_env(key, value)
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ clear_env :: proc() {
|
||||
// environ returns a copy of strings representing the environment, in the form "key=value"
|
||||
// NOTE: the slice of strings and the strings with be allocated using the supplied allocator
|
||||
@(require_results)
|
||||
environ :: proc(allocator: runtime.Allocator) -> []string {
|
||||
environ :: proc(allocator: runtime.Allocator) -> ([]string, Error) {
|
||||
return _environ(allocator)
|
||||
}
|
||||
|
||||
|
||||
+40
-40
@@ -20,19 +20,18 @@ NOT_FOUND :: -1
|
||||
// the environment is a 0 delimited list of <key>=<value> strings
|
||||
_env: [dynamic]string
|
||||
|
||||
_env_mutex: sync.Mutex
|
||||
_env_mutex: sync.Recursive_Mutex
|
||||
|
||||
// We need to be able to figure out if the environment variable
|
||||
// is contained in the original environment or not. This also
|
||||
// serves as a flag to determine if we have built _env.
|
||||
_org_env_begin: uintptr
|
||||
_org_env_end: uintptr
|
||||
_org_env_begin: uintptr // atomic
|
||||
_org_env_end: uintptr // guarded by _env_mutex
|
||||
|
||||
// Returns value + index location into _env
|
||||
// or -1 if not found
|
||||
_lookup :: proc(key: string) -> (value: string, idx: int) {
|
||||
sync.mutex_lock(&_env_mutex)
|
||||
defer sync.mutex_unlock(&_env_mutex)
|
||||
sync.guard(&_env_mutex)
|
||||
|
||||
for entry, i in _env {
|
||||
if k, v := _kv_from_entry(entry); k == key {
|
||||
@@ -43,7 +42,7 @@ _lookup :: proc(key: string) -> (value: string, idx: int) {
|
||||
}
|
||||
|
||||
_lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string, found: bool) {
|
||||
if _org_env_begin == 0 {
|
||||
if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) == 0 {
|
||||
_build_env()
|
||||
}
|
||||
|
||||
@@ -54,19 +53,18 @@ _lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string
|
||||
return
|
||||
}
|
||||
|
||||
_set_env :: proc(key, v_new: string) -> bool {
|
||||
if _org_env_begin == 0 {
|
||||
_set_env :: proc(key, v_new: string) -> Error {
|
||||
if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) == 0 {
|
||||
_build_env()
|
||||
}
|
||||
sync.guard(&_env_mutex)
|
||||
|
||||
// all key values are stored as "key=value\x00"
|
||||
kv_size := len(key) + len(v_new) + 2
|
||||
if v_curr, idx := _lookup(key); idx != NOT_FOUND {
|
||||
if v_curr == v_new {
|
||||
return true
|
||||
return nil
|
||||
}
|
||||
sync.mutex_lock(&_env_mutex)
|
||||
defer sync.mutex_unlock(&_env_mutex)
|
||||
|
||||
unordered_remove(&_env, idx)
|
||||
|
||||
@@ -78,7 +76,7 @@ _set_env :: proc(key, v_new: string) -> bool {
|
||||
if len(v_new) > len(v_curr) {
|
||||
k_addr = ([^]u8)(runtime.heap_resize(k_addr, kv_size))
|
||||
if k_addr == nil {
|
||||
return false
|
||||
return .Out_Of_Memory
|
||||
}
|
||||
v_addr = &k_addr[len(key) + 1]
|
||||
}
|
||||
@@ -86,13 +84,13 @@ _set_env :: proc(key, v_new: string) -> bool {
|
||||
v_addr[len(v_new)] = 0
|
||||
|
||||
append(&_env, string(k_addr[:kv_size]))
|
||||
return true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
k_addr := ([^]u8)(runtime.heap_alloc(kv_size))
|
||||
if k_addr == nil {
|
||||
return false
|
||||
return .Out_Of_Memory
|
||||
}
|
||||
intrinsics.mem_copy_non_overlapping(k_addr, raw_data(key), len(key))
|
||||
k_addr[len(key)] = '='
|
||||
@@ -101,16 +99,15 @@ _set_env :: proc(key, v_new: string) -> bool {
|
||||
intrinsics.mem_copy_non_overlapping(&val_slice[0], raw_data(v_new), len(v_new))
|
||||
val_slice[len(v_new)] = 0
|
||||
|
||||
sync.mutex_lock(&_env_mutex)
|
||||
append(&_env, string(k_addr[:kv_size - 1]))
|
||||
sync.mutex_unlock(&_env_mutex)
|
||||
return true
|
||||
return nil
|
||||
}
|
||||
|
||||
_unset_env :: proc(key: string) -> bool {
|
||||
if _org_env_begin == 0 {
|
||||
if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) == 0 {
|
||||
_build_env()
|
||||
}
|
||||
sync.guard(&_env_mutex)
|
||||
|
||||
v: string
|
||||
i: int
|
||||
@@ -118,15 +115,13 @@ _unset_env :: proc(key: string) -> bool {
|
||||
return false
|
||||
}
|
||||
|
||||
sync.mutex_lock(&_env_mutex)
|
||||
unordered_remove(&_env, i)
|
||||
sync.mutex_unlock(&_env_mutex)
|
||||
|
||||
if _is_in_org_env(v) {
|
||||
return true
|
||||
}
|
||||
|
||||
// if we got this far, the envrionment variable
|
||||
// if we got this far, the environment variable
|
||||
// existed AND was allocated by us.
|
||||
k_addr, _ := _kv_addr_from_val(v, key)
|
||||
runtime.heap_free(k_addr)
|
||||
@@ -134,8 +129,7 @@ _unset_env :: proc(key: string) -> bool {
|
||||
}
|
||||
|
||||
_clear_env :: proc() {
|
||||
sync.mutex_lock(&_env_mutex)
|
||||
defer sync.mutex_unlock(&_env_mutex)
|
||||
sync.guard(&_env_mutex)
|
||||
|
||||
for kv in _env {
|
||||
if !_is_in_org_env(kv) {
|
||||
@@ -145,28 +139,36 @@ _clear_env :: proc() {
|
||||
clear(&_env)
|
||||
|
||||
// nothing resides in the original environment either
|
||||
_org_env_begin = ~uintptr(0)
|
||||
intrinsics.atomic_store_explicit(&_org_env_begin, ~uintptr(0), .Release)
|
||||
_org_env_end = ~uintptr(0)
|
||||
}
|
||||
|
||||
_environ :: proc(allocator: runtime.Allocator) -> []string {
|
||||
if _org_env_begin == 0 {
|
||||
_environ :: proc(allocator: runtime.Allocator) -> (environ: []string, err: Error) {
|
||||
if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) == 0 {
|
||||
_build_env()
|
||||
}
|
||||
env := make([]string, len(_env), allocator)
|
||||
sync.guard(&_env_mutex)
|
||||
|
||||
sync.mutex_lock(&_env_mutex)
|
||||
defer sync.mutex_unlock(&_env_mutex)
|
||||
for entry, i in _env {
|
||||
env[i], _ = clone_string(entry, allocator)
|
||||
env := make([dynamic]string, 0, len(_env), allocator) or_return
|
||||
defer if err != nil {
|
||||
for e in env {
|
||||
delete(e, allocator)
|
||||
}
|
||||
delete(env)
|
||||
}
|
||||
return env
|
||||
|
||||
for entry in _env {
|
||||
s := clone_string(entry, allocator) or_return
|
||||
append(&env, s)
|
||||
}
|
||||
environ = env[:]
|
||||
return
|
||||
}
|
||||
|
||||
// The entire environment is stored as 0 terminated strings,
|
||||
// so there is no need to clone/free individual variables
|
||||
export_cstring_environment :: proc(allocator: runtime.Allocator) -> []cstring {
|
||||
if _org_env_begin == 0 {
|
||||
if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) == 0 {
|
||||
// The environment has not been modified, so we can just
|
||||
// send the original environment
|
||||
org_env := _get_original_env()
|
||||
@@ -174,12 +176,11 @@ export_cstring_environment :: proc(allocator: runtime.Allocator) -> []cstring {
|
||||
for ; org_env[n] != nil; n += 1 {}
|
||||
return slice.clone(org_env[:n + 1], allocator)
|
||||
}
|
||||
sync.guard(&_env_mutex)
|
||||
|
||||
// NOTE: already terminated by nil pointer via + 1
|
||||
env := make([]cstring, len(_env) + 1, allocator)
|
||||
|
||||
sync.mutex_lock(&_env_mutex)
|
||||
defer sync.mutex_unlock(&_env_mutex)
|
||||
for entry, i in _env {
|
||||
env[i] = cstring(raw_data(entry))
|
||||
}
|
||||
@@ -187,15 +188,14 @@ export_cstring_environment :: proc(allocator: runtime.Allocator) -> []cstring {
|
||||
}
|
||||
|
||||
_build_env :: proc() {
|
||||
sync.mutex_lock(&_env_mutex)
|
||||
defer sync.mutex_unlock(&_env_mutex)
|
||||
if _org_env_begin != 0 {
|
||||
sync.guard(&_env_mutex)
|
||||
if intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
_env = make(type_of(_env), runtime.heap_allocator())
|
||||
cstring_env := _get_original_env()
|
||||
_org_env_begin = uintptr(rawptr(cstring_env[0]))
|
||||
intrinsics.atomic_store_explicit(&_org_env_begin, uintptr(rawptr(cstring_env[0])), .Release)
|
||||
for i := 0; cstring_env[i] != nil; i += 1 {
|
||||
bytes := ([^]u8)(cstring_env[i])
|
||||
n := len(cstring_env[i])
|
||||
@@ -227,5 +227,5 @@ _kv_addr_from_val :: #force_inline proc(val: string, key: string) -> ([^]u8, [^]
|
||||
|
||||
_is_in_org_env :: #force_inline proc(env_data: string) -> bool {
|
||||
addr := uintptr(raw_data(env_data))
|
||||
return addr >= _org_env_begin && addr < _org_env_end
|
||||
return addr >= intrinsics.atomic_load_explicit(&_org_env_begin, .Acquire) && addr < _org_env_end
|
||||
}
|
||||
|
||||
+16
-14
@@ -26,13 +26,15 @@ _lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string
|
||||
return
|
||||
}
|
||||
|
||||
_set_env :: proc(key, value: string) -> (ok: bool) {
|
||||
_set_env :: proc(key, value: string) -> (err: Error) {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
|
||||
ckey := strings.clone_to_cstring(key, temp_allocator())
|
||||
cval := strings.clone_to_cstring(key, temp_allocator())
|
||||
ckey := strings.clone_to_cstring(key, temp_allocator()) or_return
|
||||
cval := strings.clone_to_cstring(key, temp_allocator()) or_return
|
||||
|
||||
ok = posix.setenv(ckey, cval, true) == .OK
|
||||
if posix.setenv(ckey, cval, true) != nil {
|
||||
err = _get_platform_error_from_errno()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -54,23 +56,23 @@ _clear_env :: proc() {
|
||||
}
|
||||
}
|
||||
|
||||
_environ :: proc(allocator: runtime.Allocator) -> (environ: []string) {
|
||||
n := 0
|
||||
_environ :: proc(allocator: runtime.Allocator) -> (environ: []string, err: Error) {
|
||||
n := 0
|
||||
for entry := posix.environ[0]; entry != nil; n, entry = n+1, posix.environ[n] {}
|
||||
|
||||
err: runtime.Allocator_Error
|
||||
if environ, err = make([]string, n, allocator); err != nil {
|
||||
// NOTE(laytan): is the environment empty or did allocation fail, how does the user know?
|
||||
return
|
||||
r := make([dynamic]string, 0, n, allocator) or_return
|
||||
defer if err != nil {
|
||||
for e in r {
|
||||
delete(e, allocator)
|
||||
}
|
||||
delete(r)
|
||||
}
|
||||
|
||||
for i, entry := 0, posix.environ[0]; entry != nil; i, entry = i+1, posix.environ[i] {
|
||||
if environ[i], err = strings.clone(string(entry), allocator); err != nil {
|
||||
// NOTE(laytan): is the entire environment returned or did allocation fail, how does the user know?
|
||||
return
|
||||
}
|
||||
append(&r, strings.clone(string(entry), allocator) or_return)
|
||||
}
|
||||
|
||||
environ = r[:]
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
+19
-46
@@ -80,46 +80,30 @@ _lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
_set_env :: proc(key, value: string) -> bool {
|
||||
if err := build_env(); err != nil {
|
||||
return false
|
||||
}
|
||||
_set_env :: proc(key, value: string) -> (err: Error) {
|
||||
build_env() or_return
|
||||
|
||||
sync.guard(&g_env_mutex)
|
||||
|
||||
key_ptr, value_ptr, just_inserted, err := map_entry(&g_env, key)
|
||||
if err != nil {
|
||||
return false
|
||||
defer if err != nil {
|
||||
delete_key(&g_env, key)
|
||||
}
|
||||
|
||||
alloc_err: runtime.Allocator_Error
|
||||
key_ptr, value_ptr, just_inserted := map_entry(&g_env, key) or_return
|
||||
|
||||
if just_inserted {
|
||||
key_ptr^, alloc_err = clone_string(key, file_allocator())
|
||||
if alloc_err != nil {
|
||||
delete_key(&g_env, key)
|
||||
return false
|
||||
}
|
||||
|
||||
value_ptr^, alloc_err = clone_string(value, file_allocator())
|
||||
if alloc_err != nil {
|
||||
delete_key(&g_env, key)
|
||||
key_ptr^ = clone_string(key, file_allocator()) or_return
|
||||
defer if err != nil {
|
||||
delete(key_ptr^, file_allocator())
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
value_ptr^ = clone_string(value, file_allocator()) or_return
|
||||
return
|
||||
}
|
||||
|
||||
delete_string_if_not_original(value_ptr^)
|
||||
|
||||
value_ptr^, alloc_err = clone_string(value, file_allocator())
|
||||
if alloc_err != nil {
|
||||
delete_key(&g_env, key)
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
value_ptr^ = clone_string(value, file_allocator()) or_return
|
||||
return
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
@@ -153,34 +137,23 @@ _clear_env :: proc() {
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
_environ :: proc(allocator: runtime.Allocator) -> []string {
|
||||
if err := build_env(); err != nil {
|
||||
return nil
|
||||
}
|
||||
_environ :: proc(allocator: runtime.Allocator) -> (environ: []string, err: Error) {
|
||||
build_env() or_return
|
||||
|
||||
sync.shared_guard(&g_env_mutex)
|
||||
|
||||
envs, alloc_err := make([]string, len(g_env), allocator)
|
||||
if alloc_err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
defer if alloc_err != nil {
|
||||
envs := make([dynamic]string, 0, len(g_env), allocator) or_return
|
||||
defer if err != nil {
|
||||
for env in envs {
|
||||
delete(env, allocator)
|
||||
}
|
||||
delete(envs, allocator)
|
||||
delete(envs)
|
||||
}
|
||||
|
||||
i: int
|
||||
for k, v in g_env {
|
||||
defer i += 1
|
||||
|
||||
envs[i], alloc_err = concatenate({k, "=", v}, allocator)
|
||||
if alloc_err != nil {
|
||||
return nil
|
||||
}
|
||||
append(&envs, concatenate({k, "=", v}, allocator) or_return)
|
||||
}
|
||||
|
||||
return envs
|
||||
environ = envs[:]
|
||||
return
|
||||
}
|
||||
|
||||
@@ -36,12 +36,15 @@ _lookup_env :: proc(key: string, allocator: runtime.Allocator) -> (value: string
|
||||
return
|
||||
}
|
||||
|
||||
_set_env :: proc(key, value: string) -> bool {
|
||||
_set_env :: proc(key, value: string) -> Error {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
k, _ := win32_utf8_to_wstring(key, temp_allocator())
|
||||
v, _ := win32_utf8_to_wstring(value, temp_allocator())
|
||||
k := win32_utf8_to_wstring(key, temp_allocator()) or_return
|
||||
v := win32_utf8_to_wstring(value, temp_allocator()) or_return
|
||||
|
||||
return bool(win32.SetEnvironmentVariableW(k, v))
|
||||
if !win32.SetEnvironmentVariableW(k, v) {
|
||||
return _get_platform_error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_unset_env :: proc(key: string) -> bool {
|
||||
@@ -52,7 +55,7 @@ _unset_env :: proc(key: string) -> bool {
|
||||
|
||||
_clear_env :: proc() {
|
||||
TEMP_ALLOCATOR_GUARD()
|
||||
envs := environ(temp_allocator())
|
||||
envs, _ := environ(temp_allocator())
|
||||
for env in envs {
|
||||
for j in 1..<len(env) {
|
||||
if env[j] == '=' {
|
||||
@@ -63,10 +66,10 @@ _clear_env :: proc() {
|
||||
}
|
||||
}
|
||||
|
||||
_environ :: proc(allocator: runtime.Allocator) -> []string {
|
||||
_environ :: proc(allocator: runtime.Allocator) -> (environ: []string, err: Error) {
|
||||
envs := win32.GetEnvironmentStringsW()
|
||||
if envs == nil {
|
||||
return nil
|
||||
return
|
||||
}
|
||||
defer win32.FreeEnvironmentStringsW(envs)
|
||||
|
||||
@@ -82,7 +85,13 @@ _environ :: proc(allocator: runtime.Allocator) -> []string {
|
||||
}
|
||||
}
|
||||
|
||||
r := make([dynamic]string, 0, n, allocator)
|
||||
r := make([dynamic]string, 0, n, allocator) or_return
|
||||
defer if err != nil {
|
||||
for e in r {
|
||||
delete(e, allocator)
|
||||
}
|
||||
delete(r)
|
||||
}
|
||||
for from, i, p := 0, 0, envs; true; i += 1 {
|
||||
c := ([^]u16)(p)[i]
|
||||
if c == 0 {
|
||||
@@ -90,12 +99,14 @@ _environ :: proc(allocator: runtime.Allocator) -> []string {
|
||||
break
|
||||
}
|
||||
w := ([^]u16)(p)[from:i]
|
||||
append(&r, win32_utf16_to_utf8(w, allocator) or_else "")
|
||||
s := win32_utf16_to_utf8(w, allocator) or_return
|
||||
append(&r, s)
|
||||
from = i + 1
|
||||
}
|
||||
}
|
||||
|
||||
return r[:]
|
||||
environ = r[:]
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -122,6 +122,11 @@ new_file :: proc(handle: uintptr, name: string) -> ^File {
|
||||
return file
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
clone :: proc(f: ^File) -> (^File, Error) {
|
||||
return _clone(f)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
fd :: proc(f: ^File) -> uintptr {
|
||||
return _fd(f)
|
||||
|
||||
@@ -113,6 +113,23 @@ _new_file :: proc(fd: uintptr, _: string, allocator: runtime.Allocator) -> (f: ^
|
||||
return &impl.file, nil
|
||||
}
|
||||
|
||||
_clone :: proc(f: ^File) -> (clone: ^File, err: Error) {
|
||||
if f == nil || f.impl == nil {
|
||||
return
|
||||
}
|
||||
|
||||
fd := (^File_Impl)(f.impl).fd
|
||||
|
||||
clonefd, errno := linux.dup(fd)
|
||||
if errno != nil {
|
||||
err = _get_platform_error(errno)
|
||||
return
|
||||
}
|
||||
defer if err != nil { linux.close(clonefd) }
|
||||
|
||||
return _new_file(uintptr(clonefd), "", file_allocator())
|
||||
}
|
||||
|
||||
|
||||
@(require_results)
|
||||
_open_buffered :: proc(name: string, buffer_size: uint, flags := File_Flags{.Read}, perm := 0o777) -> (f: ^File, err: Error) {
|
||||
|
||||
@@ -114,6 +114,29 @@ __new_file :: proc(handle: posix.FD, allocator: runtime.Allocator) -> ^File {
|
||||
return &impl.file
|
||||
}
|
||||
|
||||
_clone :: proc(f: ^File) -> (clone: ^File, err: Error) {
|
||||
if f == nil || f.impl == nil {
|
||||
err = .Invalid_Pointer
|
||||
return
|
||||
}
|
||||
|
||||
impl := (^File_Impl)(f.impl)
|
||||
|
||||
fd := posix.dup(impl.fd)
|
||||
if fd <= 0 {
|
||||
err = _get_platform_error()
|
||||
return
|
||||
}
|
||||
defer if err != nil { posix.close(fd) }
|
||||
|
||||
clone = __new_file(fd, file_allocator())
|
||||
clone_impl := (^File_Impl)(clone.impl)
|
||||
clone_impl.cname = clone_to_cstring(impl.name, file_allocator()) or_return
|
||||
clone_impl.name = string(clone_impl.cname)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
_close :: proc(f: ^File_Impl) -> (err: Error) {
|
||||
if f == nil { return nil }
|
||||
|
||||
|
||||
@@ -223,6 +223,32 @@ _new_file :: proc(handle: uintptr, name: string, allocator: runtime.Allocator) -
|
||||
return &impl.file, nil
|
||||
}
|
||||
|
||||
_clone :: proc(f: ^File) -> (clone: ^File, err: Error) {
|
||||
if f == nil || f.impl == nil {
|
||||
return
|
||||
}
|
||||
|
||||
dir_fd, relative, ok := match_preopen(name(f))
|
||||
if !ok {
|
||||
return nil, .Invalid_Path
|
||||
}
|
||||
|
||||
fd, fderr := wasi.path_open(dir_fd, {.SYMLINK_FOLLOW}, relative, {}, {}, {}, {})
|
||||
if fderr != nil {
|
||||
err = _get_platform_error(fderr)
|
||||
return
|
||||
}
|
||||
defer if err != nil { wasi.fd_close(fd) }
|
||||
|
||||
fderr = wasi.fd_renumber((^File_Impl)(f.impl).fd, fd)
|
||||
if fderr != nil {
|
||||
err = _get_platform_error(fderr)
|
||||
return
|
||||
}
|
||||
|
||||
return _new_file(uintptr(fd), name(f), file_allocator())
|
||||
}
|
||||
|
||||
_close :: proc(f: ^File_Impl) -> (err: Error) {
|
||||
if errno := wasi.fd_close(f.fd); errno != nil {
|
||||
err = _get_platform_error(errno)
|
||||
|
||||
@@ -210,6 +210,29 @@ _new_file_buffered :: proc(handle: uintptr, name: string, buffer_size: uint) ->
|
||||
return
|
||||
}
|
||||
|
||||
_clone :: proc(f: ^File) -> (clone: ^File, err: Error) {
|
||||
if f == nil || f.impl == nil {
|
||||
return
|
||||
}
|
||||
|
||||
clonefd: win32.HANDLE
|
||||
process := win32.GetCurrentProcess()
|
||||
if !win32.DuplicateHandle(
|
||||
process,
|
||||
win32.HANDLE(_fd(f)),
|
||||
process,
|
||||
&clonefd,
|
||||
0,
|
||||
false,
|
||||
win32.DUPLICATE_SAME_ACCESS,
|
||||
) {
|
||||
err = _get_platform_error()
|
||||
return
|
||||
}
|
||||
defer if err != nil { win32.CloseHandle(clonefd) }
|
||||
|
||||
return _new_file(uintptr(clonefd), name(f), file_allocator())
|
||||
}
|
||||
|
||||
_fd :: proc(f: ^File) -> uintptr {
|
||||
if f == nil || f.impl == nil {
|
||||
|
||||
@@ -3,6 +3,7 @@ package os2
|
||||
|
||||
import "base:intrinsics"
|
||||
import "base:runtime"
|
||||
import "core:math/rand"
|
||||
|
||||
|
||||
// Splits pattern by the last wildcard "*", if it exists, and returns the prefix and suffix
|
||||
@@ -84,45 +85,15 @@ concatenate :: proc(strings: []string, allocator: runtime.Allocator) -> (res: st
|
||||
return string(buf), nil
|
||||
}
|
||||
|
||||
|
||||
|
||||
@(private="file")
|
||||
random_string_seed: [2]u64
|
||||
|
||||
@(init, private="file")
|
||||
init_random_string_seed :: proc() {
|
||||
seed := u64(intrinsics.read_cycle_counter())
|
||||
s := &random_string_seed
|
||||
s[0] = 0
|
||||
s[1] = (seed << 1) | 1
|
||||
_ = next_random(s)
|
||||
s[1] += seed
|
||||
_ = next_random(s)
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
next_random :: proc(r: ^[2]u64) -> u64 {
|
||||
old_state := r[0]
|
||||
r[0] = old_state * 6364136223846793005 + (r[1]|1)
|
||||
xor_shifted := (((old_state >> 59) + 5) ~ old_state) * 12605985483714917081
|
||||
rot := (old_state >> 59)
|
||||
return (xor_shifted >> rot) | (xor_shifted << ((-rot) & 63))
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
random_string :: proc(buf: []byte) -> string {
|
||||
@(static, rodata) digits := "0123456789"
|
||||
|
||||
u := next_random(&random_string_seed)
|
||||
|
||||
b :: 10
|
||||
i := len(buf)
|
||||
for u >= b {
|
||||
i -= 1
|
||||
buf[i] = digits[u % b]
|
||||
u /= b
|
||||
for i := 0; i < len(buf); i += 16 {
|
||||
n := rand.uint64()
|
||||
end := min(i + 16, len(buf))
|
||||
for j := i; j < end; j += 1 {
|
||||
buf[j] = '0' + u8(n) % 10
|
||||
n >>= 4
|
||||
}
|
||||
}
|
||||
i -= 1
|
||||
buf[i] = digits[u % b]
|
||||
return string(buf[i:])
|
||||
return string(buf)
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ _get_executable_path :: proc(allocator: runtime.Allocator) -> (path: string, err
|
||||
strings.write_string(&buf, "/")
|
||||
strings.write_string(&buf, sarg)
|
||||
|
||||
cpath := strings.to_cstring(&buf)
|
||||
cpath := strings.to_cstring(&buf) or_return
|
||||
if posix.access(cpath, {.X_OK}) == .OK {
|
||||
return real(cpath, allocator)
|
||||
}
|
||||
|
||||
@@ -60,16 +60,20 @@ _remove_all :: proc(path: string) -> (err: Error) {
|
||||
dir := open(path) or_return
|
||||
defer close(dir)
|
||||
|
||||
iter := read_directory_iterator_create(dir) or_return
|
||||
iter := read_directory_iterator_create(dir)
|
||||
defer read_directory_iterator_destroy(&iter)
|
||||
|
||||
for fi in read_directory_iterator(&iter) {
|
||||
_ = read_directory_iterator_error(&iter) or_break
|
||||
|
||||
if fi.type == .Directory {
|
||||
_remove_all(fi.fullpath) or_return
|
||||
} else {
|
||||
remove(fi.fullpath) or_return
|
||||
}
|
||||
}
|
||||
|
||||
_ = read_directory_iterator_error(&iter) or_return
|
||||
}
|
||||
|
||||
return remove(path)
|
||||
|
||||
@@ -29,7 +29,7 @@ _pipe :: proc() -> (r, w: ^File, err: Error) {
|
||||
strings.write_string(&rname, "/dev/fd/")
|
||||
strings.write_int(&rname, int(fds[0]))
|
||||
ri.name = strings.to_string(rname)
|
||||
ri.cname = strings.to_cstring(&rname)
|
||||
ri.cname = strings.to_cstring(&rname) or_return
|
||||
|
||||
w = __new_file(fds[1], file_allocator())
|
||||
wi := (^File_Impl)(w.impl)
|
||||
@@ -39,7 +39,7 @@ _pipe :: proc() -> (r, w: ^File, err: Error) {
|
||||
strings.write_string(&wname, "/dev/fd/")
|
||||
strings.write_int(&wname, int(fds[1]))
|
||||
wi.name = strings.to_string(wname)
|
||||
wi.cname = strings.to_cstring(&wname)
|
||||
wi.cname = strings.to_cstring(&wname) or_return
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
|
||||
|
||||
strings.write_string(&path_builder, "/proc/")
|
||||
strings.write_int(&path_builder, pid)
|
||||
proc_fd, errno := linux.open(strings.to_cstring(&path_builder), _OPENDIR_FLAGS)
|
||||
proc_fd, errno := linux.open(strings.to_cstring(&path_builder) or_return, _OPENDIR_FLAGS)
|
||||
if errno != .NONE {
|
||||
err = _get_platform_error(errno)
|
||||
return
|
||||
@@ -169,7 +169,7 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
|
||||
strings.write_int(&path_builder, pid)
|
||||
strings.write_string(&path_builder, "/cmdline")
|
||||
|
||||
cmdline_bytes, cmdline_err := _read_entire_pseudo_file(strings.to_cstring(&path_builder), temp_allocator())
|
||||
cmdline_bytes, cmdline_err := _read_entire_pseudo_file(strings.to_cstring(&path_builder) or_return, temp_allocator())
|
||||
if cmdline_err != nil || len(cmdline_bytes) == 0 {
|
||||
err = cmdline_err
|
||||
break cmdline_if
|
||||
@@ -190,7 +190,7 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
|
||||
strings.write_int(&path_builder, pid)
|
||||
strings.write_string(&path_builder, "/cwd")
|
||||
|
||||
cwd, cwd_err = _read_link_cstr(strings.to_cstring(&path_builder), temp_allocator()) // allowed to fail
|
||||
cwd, cwd_err = _read_link_cstr(strings.to_cstring(&path_builder) or_return, temp_allocator()) // allowed to fail
|
||||
if cwd_err == nil && .Working_Dir in selection {
|
||||
info.working_dir = strings.clone(cwd, allocator) or_return
|
||||
info.fields += {.Working_Dir}
|
||||
@@ -258,7 +258,7 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
|
||||
strings.write_int(&path_builder, pid)
|
||||
strings.write_string(&path_builder, "/stat")
|
||||
|
||||
proc_stat_bytes, stat_err := _read_entire_pseudo_file(strings.to_cstring(&path_builder), temp_allocator())
|
||||
proc_stat_bytes, stat_err := _read_entire_pseudo_file(strings.to_cstring(&path_builder) or_return, temp_allocator())
|
||||
if stat_err != nil {
|
||||
err = stat_err
|
||||
break stat_if
|
||||
@@ -330,7 +330,7 @@ _process_info_by_pid :: proc(pid: int, selection: Process_Info_Fields, allocator
|
||||
strings.write_int(&path_builder, pid)
|
||||
strings.write_string(&path_builder, "/environ")
|
||||
|
||||
if env_bytes, env_err := _read_entire_pseudo_file(strings.to_cstring(&path_builder), temp_allocator()); env_err == nil {
|
||||
if env_bytes, env_err := _read_entire_pseudo_file(strings.to_cstring(&path_builder) or_return, temp_allocator()); env_err == nil {
|
||||
env := string(env_bytes)
|
||||
|
||||
env_list := make([dynamic]string, allocator) or_return
|
||||
@@ -418,7 +418,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
|
||||
strings.write_byte(&exe_builder, '/')
|
||||
strings.write_string(&exe_builder, executable_name)
|
||||
|
||||
exe_path = strings.to_cstring(&exe_builder)
|
||||
exe_path = strings.to_cstring(&exe_builder) or_return
|
||||
if linux.access(exe_path, linux.X_OK) == .NONE {
|
||||
found = true
|
||||
break
|
||||
@@ -430,7 +430,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
|
||||
strings.write_string(&exe_builder, "./")
|
||||
strings.write_string(&exe_builder, executable_name)
|
||||
|
||||
exe_path = strings.to_cstring(&exe_builder)
|
||||
exe_path = strings.to_cstring(&exe_builder) or_return
|
||||
if linux.access(exe_path, linux.X_OK) != .NONE {
|
||||
return process, .Not_Exist
|
||||
}
|
||||
@@ -594,7 +594,7 @@ _process_state_update_times :: proc(state: ^Process_State) -> (err: Error) {
|
||||
strings.write_string(&path_builder, "/stat")
|
||||
|
||||
stat_buf: []u8
|
||||
stat_buf, err = _read_entire_pseudo_file(strings.to_cstring(&path_builder), temp_allocator())
|
||||
stat_buf, err = _read_entire_pseudo_file(strings.to_cstring(&path_builder) or_return, temp_allocator())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
|
||||
strings.write_byte(&exe_builder, '/')
|
||||
strings.write_string(&exe_builder, exe_name)
|
||||
|
||||
if exe_fd := posix.open(strings.to_cstring(&exe_builder), {.CLOEXEC, .EXEC}); exe_fd == -1 {
|
||||
if exe_fd := posix.open(strings.to_cstring(&exe_builder) or_return, {.CLOEXEC, .EXEC}); exe_fd == -1 {
|
||||
continue
|
||||
} else {
|
||||
posix.close(exe_fd)
|
||||
@@ -91,7 +91,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
|
||||
|
||||
// "hello/./world" is fine right?
|
||||
|
||||
if exe_fd := posix.open(strings.to_cstring(&exe_builder), {.CLOEXEC, .EXEC}); exe_fd == -1 {
|
||||
if exe_fd := posix.open(strings.to_cstring(&exe_builder) or_return, {.CLOEXEC, .EXEC}); exe_fd == -1 {
|
||||
err = .Not_Exist
|
||||
return
|
||||
} else {
|
||||
@@ -102,7 +102,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
|
||||
strings.builder_reset(&exe_builder)
|
||||
strings.write_string(&exe_builder, exe_name)
|
||||
|
||||
if exe_fd := posix.open(strings.to_cstring(&exe_builder), {.CLOEXEC, .EXEC}); exe_fd == -1 {
|
||||
if exe_fd := posix.open(strings.to_cstring(&exe_builder) or_return, {.CLOEXEC, .EXEC}); exe_fd == -1 {
|
||||
err = .Not_Exist
|
||||
return
|
||||
} else {
|
||||
@@ -181,7 +181,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
|
||||
if posix.chdir(cwd) != .OK { abort(pipe[WRITE]) }
|
||||
}
|
||||
|
||||
res := posix.execve(strings.to_cstring(&exe_builder), raw_data(cmd), env)
|
||||
res := posix.execve(strings.to_cstring(&exe_builder) or_return, raw_data(cmd), env)
|
||||
assert(res == -1)
|
||||
abort(pipe[WRITE])
|
||||
|
||||
|
||||
@@ -427,7 +427,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
|
||||
command_line_w := win32_utf8_to_wstring(command_line, temp_allocator()) or_return
|
||||
environment := desc.env
|
||||
if desc.env == nil {
|
||||
environment = environ(temp_allocator())
|
||||
environment = environ(temp_allocator()) or_return
|
||||
}
|
||||
environment_block := _build_environment_block(environment, temp_allocator())
|
||||
environment_block_w := win32_utf8_to_utf16(environment_block, temp_allocator()) or_return
|
||||
|
||||
@@ -24,7 +24,7 @@ File_Info :: struct {
|
||||
@(require_results)
|
||||
file_info_clone :: proc(fi: File_Info, allocator: runtime.Allocator) -> (cloned: File_Info, err: runtime.Allocator_Error) {
|
||||
cloned = fi
|
||||
cloned.fullpath = strings.clone(fi.fullpath) or_return
|
||||
cloned.fullpath = strings.clone(fi.fullpath, allocator) or_return
|
||||
cloned.name = filepath.base(cloned.fullpath)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ create_temp_file :: proc(dir, pattern: string) -> (f: ^File, err: Error) {
|
||||
prefix, suffix := _prefix_and_suffix(pattern) or_return
|
||||
prefix = temp_join_path(dir, prefix) or_return
|
||||
|
||||
rand_buf: [32]byte
|
||||
rand_buf: [10]byte
|
||||
name_buf := make([]byte, len(prefix)+len(rand_buf)+len(suffix), temp_allocator())
|
||||
|
||||
attempts := 0
|
||||
@@ -52,7 +52,7 @@ make_directory_temp :: proc(dir, pattern: string, allocator: runtime.Allocator)
|
||||
prefix, suffix := _prefix_and_suffix(pattern) or_return
|
||||
prefix = temp_join_path(dir, prefix) or_return
|
||||
|
||||
rand_buf: [32]byte
|
||||
rand_buf: [10]byte
|
||||
name_buf := make([]byte, len(prefix)+len(rand_buf)+len(suffix), temp_allocator())
|
||||
|
||||
attempts := 0
|
||||
|
||||
@@ -49,7 +49,7 @@ user_config_dir :: proc(allocator: runtime.Allocator) -> (dir: string, err: Erro
|
||||
dir = concatenate({dir, "/.config"}, allocator) or_return
|
||||
}
|
||||
case: // All other UNIX systems
|
||||
dir = get_env("XDG_CACHE_HOME", allocator)
|
||||
dir = get_env("XDG_CONFIG_HOME", allocator)
|
||||
if dir == "" {
|
||||
dir = get_env("HOME", temp_allocator())
|
||||
if dir == "" {
|
||||
|
||||
+2285
-38
File diff suppressed because it is too large
Load Diff
@@ -898,8 +898,7 @@ bitset_to_enum_slice_with_buffer :: proc(buf: []$E, bs: $T) -> (slice: []E) wher
|
||||
// sl := slice.bitset_to_enum_slice(bs)
|
||||
@(require_results)
|
||||
bitset_to_enum_slice_with_make :: proc(bs: $T, $E: typeid, allocator := context.allocator) -> (slice: []E) where intrinsics.type_is_enum(E), intrinsics.type_bit_set_elem_type(T) == E {
|
||||
ones := intrinsics.count_ones(transmute(E)bs)
|
||||
buf := make([]E, int(ones), allocator)
|
||||
buf := make([]E, card(bs), allocator)
|
||||
return bitset_to_enum_slice(buf, bs)
|
||||
}
|
||||
|
||||
|
||||
@@ -288,18 +288,41 @@ to_string :: proc(b: Builder) -> (res: string) {
|
||||
/*
|
||||
Appends a trailing null byte after the end of the current Builder byte buffer and then casts it to a cstring
|
||||
|
||||
NOTE: This procedure will not check if the backing buffer has enough space to include the extra null byte.
|
||||
|
||||
Inputs:
|
||||
- b: A pointer to builder
|
||||
|
||||
Returns:
|
||||
- res: A cstring of the Builder's buffer
|
||||
*/
|
||||
to_cstring :: proc(b: ^Builder) -> (res: cstring) {
|
||||
unsafe_to_cstring :: proc(b: ^Builder) -> (res: cstring) {
|
||||
append(&b.buf, 0)
|
||||
pop(&b.buf)
|
||||
return cstring(raw_data(b.buf))
|
||||
}
|
||||
/*
|
||||
Appends a trailing null byte after the end of the current Builder byte buffer and then casts it to a cstring
|
||||
|
||||
Inputs:
|
||||
- b: A pointer to builder
|
||||
|
||||
Returns:
|
||||
- res: A cstring of the Builder's buffer upon success
|
||||
- err: An optional allocator error if one occured, `nil` otherwise
|
||||
*/
|
||||
to_cstring :: proc(b: ^Builder) -> (res: cstring, err: mem.Allocator_Error) {
|
||||
n := append(&b.buf, 0) or_return
|
||||
if n != 1 {
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
pop(&b.buf)
|
||||
#no_bounds_check {
|
||||
assert(b.buf[len(b.buf)] == 0)
|
||||
}
|
||||
return cstring(raw_data(b.buf)), nil
|
||||
}
|
||||
/*
|
||||
Returns the length of the Builder's buffer, in bytes
|
||||
|
||||
Inputs:
|
||||
|
||||
@@ -25,7 +25,7 @@ Returns:
|
||||
clone :: proc(s: string, allocator := context.allocator, loc := #caller_location) -> (res: string, err: mem.Allocator_Error) #optional_allocator_error {
|
||||
c := make([]byte, len(s), allocator, loc) or_return
|
||||
copy(c, s)
|
||||
return string(c[:len(s)]), nil
|
||||
return string(c), nil
|
||||
}
|
||||
/*
|
||||
Clones a string safely (returns early with an allocation error on failure)
|
||||
|
||||
@@ -14,6 +14,6 @@ SavePanel_savePanel :: proc "c" () -> ^SavePanel {
|
||||
}
|
||||
|
||||
@(objc_type=SavePanel, objc_name="URL")
|
||||
SavePanel_URL :: proc "c" (self: ^SavePanel) -> ^Array {
|
||||
return msgSend(^Array, self, "URL")
|
||||
SavePanel_URL :: proc "c" (self: ^SavePanel) -> ^URL {
|
||||
return msgSend(^URL, self, "URL")
|
||||
}
|
||||
|
||||
+47
-30
@@ -37,12 +37,7 @@ log_info :: proc "contextless" (msg: cstring, loc := #caller_location) {
|
||||
}
|
||||
|
||||
abort :: proc "contextless" (msg: cstring, loc := #caller_location) {
|
||||
abort_ext(
|
||||
cstring(raw_data(loc.procedure)),
|
||||
cstring(raw_data(loc.file_path)),
|
||||
loc.line,
|
||||
msg,
|
||||
)
|
||||
abort_ext(cstring(raw_data(loc.procedure)), cstring(raw_data(loc.file_path)), loc.line, msg)
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -55,7 +50,12 @@ list_entry :: proc "contextless" (elt: ^list_elt, $T: typeid, $member: string) -
|
||||
}
|
||||
|
||||
// Get the next entry in a list.
|
||||
list_next_entry :: proc "contextless" (list: ^list, elt: ^list_elt, $T: typeid, $member: string) -> ^T {
|
||||
list_next_entry :: proc "contextless" (
|
||||
list: ^list,
|
||||
elt: ^list_elt,
|
||||
$T: typeid,
|
||||
$member: string,
|
||||
) -> ^T {
|
||||
if elt.next != list.last {
|
||||
return list_entry(elt.next, T, member)
|
||||
}
|
||||
@@ -64,7 +64,12 @@ list_next_entry :: proc "contextless" (list: ^list, elt: ^list_elt, $T: typeid,
|
||||
}
|
||||
|
||||
// Get the previous entry in a list.
|
||||
list_prev_entry :: proc "contextless" (list: ^list, elt: ^list_elt, $T: typeid, $member: string) -> ^T {
|
||||
list_prev_entry :: proc "contextless" (
|
||||
list: ^list,
|
||||
elt: ^list_elt,
|
||||
$T: typeid,
|
||||
$member: string,
|
||||
) -> ^T {
|
||||
if elt.prev != list.last {
|
||||
return list_entry(elt.prev, T, member)
|
||||
}
|
||||
@@ -94,9 +99,23 @@ list_last_entry :: proc "contextless" (list: ^list, $T: typeid, $member: string)
|
||||
// _elt: ^list_elt
|
||||
// for elt in oc.list_for(list, &_elt, int, "elt") {
|
||||
// }
|
||||
list_for :: proc "contextless" (list: ^list, elt: ^^list_elt, $T: typeid, $member: string) -> (^T, bool) {
|
||||
list_for :: proc "contextless" (
|
||||
list: ^list,
|
||||
elt: ^^list_elt,
|
||||
$T: typeid,
|
||||
$member: string,
|
||||
) -> (
|
||||
^T,
|
||||
bool,
|
||||
) {
|
||||
if elt == nil {
|
||||
assert_fail(#file, #procedure, #line, "elt != nil", "misuse of `list_for`, expected `elt` to not be nil")
|
||||
assert_fail(
|
||||
#file,
|
||||
#procedure,
|
||||
#line,
|
||||
"elt != nil",
|
||||
"misuse of `list_for`, expected `elt` to not be nil",
|
||||
)
|
||||
}
|
||||
|
||||
if elt^ == nil {
|
||||
@@ -112,7 +131,15 @@ list_for :: proc "contextless" (list: ^list, elt: ^^list_elt, $T: typeid, $membe
|
||||
|
||||
list_iter :: list_for
|
||||
|
||||
list_for_reverse :: proc "contextless" (list: ^list, elt: ^^list_elt, $T: typeid, $member: string) -> (^T, bool) {
|
||||
list_for_reverse :: proc "contextless" (
|
||||
list: ^list,
|
||||
elt: ^^list_elt,
|
||||
$T: typeid,
|
||||
$member: string,
|
||||
) -> (
|
||||
^T,
|
||||
bool,
|
||||
) {
|
||||
if elt^ == nil {
|
||||
elt^ = list.last
|
||||
entry := list_checked_entry(elt^, T, member)
|
||||
@@ -226,27 +253,17 @@ str32_list_for :: proc "contextless" (list: ^str32_list, elt: ^^list_elt) -> (^s
|
||||
return list_for(&list.list, elt, str32_elt, "listElt")
|
||||
}
|
||||
|
||||
@(deferred_none=ui_box_end)
|
||||
ui_container :: proc "contextless" (name: string, flags: ui_flags = {}) -> ^ui_box {
|
||||
return ui_box_begin_str8(name, flags)
|
||||
@(deferred_none = ui_box_end)
|
||||
ui_container :: proc "contextless" (name: string) -> ^ui_box {
|
||||
return ui_box_begin_str8(name)
|
||||
}
|
||||
|
||||
@(deferred_none=ui_end_frame)
|
||||
ui_frame :: proc "contextless" (frame_size: [2]f32, style: ui_style, mask: ui_style_mask) {
|
||||
ui_begin_frame(frame_size, style, mask)
|
||||
@(deferred_none = ui_menu_end)
|
||||
ui_menu :: proc "contextless" (key, name: string) {
|
||||
ui_menu_begin_str8(key, name)
|
||||
}
|
||||
|
||||
@(deferred_none=ui_panel_end)
|
||||
ui_panel :: proc "contextless" (name: cstring, flags: ui_flags) {
|
||||
ui_panel_begin(name, flags)
|
||||
}
|
||||
|
||||
@(deferred_none=ui_menu_end)
|
||||
ui_menu :: proc "contextless" (name: cstring) {
|
||||
ui_menu_begin(name)
|
||||
}
|
||||
|
||||
@(deferred_none=ui_menu_bar_end)
|
||||
ui_menu_bar :: proc "contextless" (name: cstring) {
|
||||
ui_menu_bar_begin(name)
|
||||
@(deferred_none = ui_menu_bar_end)
|
||||
ui_menu_bar :: proc "contextless" (key: string) {
|
||||
ui_menu_bar_begin_str8(key)
|
||||
}
|
||||
|
||||
+13
-3
@@ -8,15 +8,25 @@ create_odin_logger :: proc(lowest := runtime.Logger_Level.Debug, ident := "") ->
|
||||
return runtime.Logger{odin_logger_proc, nil, lowest, {}}
|
||||
}
|
||||
|
||||
log_typed :: proc "contextless" (level: log_level, msg: cstring, loc := #caller_location) {
|
||||
log_ext(
|
||||
level,
|
||||
cstring(raw_data(loc.procedure)),
|
||||
cstring(raw_data(loc.file_path)),
|
||||
loc.line,
|
||||
msg,
|
||||
)
|
||||
}
|
||||
|
||||
odin_logger_proc :: proc(logger_data: rawptr, level: runtime.Logger_Level, text: string, options: runtime.Logger_Options, location := #caller_location) {
|
||||
cbuf := make([]byte, len(text)+1, context.temp_allocator)
|
||||
copy(cbuf, text)
|
||||
ctext := cstring(raw_data(cbuf))
|
||||
|
||||
switch level {
|
||||
case .Debug, .Info: log_info(ctext, location)
|
||||
case .Warning: log_warning(ctext, location)
|
||||
case .Debug, .Info: log_typed(.INFO, ctext, location)
|
||||
case .Warning: log_typed(.WARNING, ctext, location)
|
||||
case: fallthrough
|
||||
case .Error, .Fatal: log_error(ctext, location)
|
||||
case .Error, .Fatal: log_typed(.ERROR, ctext, location)
|
||||
}
|
||||
}
|
||||
|
||||
+526
-562
File diff suppressed because it is too large
Load Diff
@@ -38,6 +38,13 @@ DWMNCRENDERINGPOLICY :: enum {
|
||||
DWMNCRP_LAST,
|
||||
}
|
||||
|
||||
DWM_WINDOW_CORNER_PREFERENCE :: enum c_int {
|
||||
DEFAULT,
|
||||
DONOTROUND,
|
||||
ROUND,
|
||||
ROUNDSMALL,
|
||||
}
|
||||
|
||||
@(default_calling_convention="system")
|
||||
foreign dwmapi {
|
||||
DwmFlush :: proc() -> HRESULT ---
|
||||
|
||||
Reference in New Issue
Block a user