Merge branch 'odin-lang:master' into master

This commit is contained in:
avanspector
2025-01-06 16:42:29 +04:00
committed by GitHub
92 changed files with 2808 additions and 1206 deletions
+1 -1
View File
@@ -115,7 +115,7 @@ open :: proc(name: string, flags := File_Flags{.Read}, perm := 0o777) -> (^File,
@(require_results)
new_file :: proc(handle: uintptr, name: string) -> ^File {
file, err := _new_file(handle, name)
file, err := _new_file(handle, name, file_allocator())
if err != nil {
panic(error_string(err))
}
+27 -54
View File
@@ -39,37 +39,23 @@ _stderr := File{
@init
_standard_stream_init :: proc() {
@static stdin_impl := File_Impl {
name = "/proc/self/fd/0",
fd = 0,
new_std :: proc(impl: ^File_Impl, fd: linux.Fd, name: string) -> ^File {
impl.file.impl = impl
impl.fd = linux.Fd(fd)
impl.allocator = runtime.nil_allocator()
impl.name = name
impl.file.stream = {
data = impl,
procedure = _file_stream_proc,
}
impl.file.fstat = _fstat
return &impl.file
}
@static stdout_impl := File_Impl {
name = "/proc/self/fd/1",
fd = 1,
}
@static stderr_impl := File_Impl {
name = "/proc/self/fd/2",
fd = 2,
}
stdin_impl.allocator = file_allocator()
stdout_impl.allocator = file_allocator()
stderr_impl.allocator = file_allocator()
_stdin.impl = &stdin_impl
_stdout.impl = &stdout_impl
_stderr.impl = &stderr_impl
// cannot define these initially because cyclic reference
_stdin.stream.data = &stdin_impl
_stdout.stream.data = &stdout_impl
_stderr.stream.data = &stderr_impl
stdin = &_stdin
stdout = &_stdout
stderr = &_stderr
@(static) files: [3]File_Impl
stdin = new_std(&files[0], 0, "/proc/self/fd/0")
stdout = new_std(&files[1], 1, "/proc/self/fd/1")
stderr = new_std(&files[2], 2, "/proc/self/fd/2")
}
_open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) {
@@ -80,6 +66,9 @@ _open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Err
// terminal would be incredibly rare. This has no effect on files while
// allowing us to open serial devices.
sys_flags: linux.Open_Flags = {.NOCTTY, .CLOEXEC}
when size_of(rawptr) == 4 {
sys_flags += {.LARGEFILE}
}
switch flags & (O_RDONLY|O_WRONLY|O_RDWR) {
case O_RDONLY:
case O_WRONLY: sys_flags += {.WRONLY}
@@ -97,18 +86,18 @@ _open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Err
return nil, _get_platform_error(errno)
}
return _new_file(uintptr(fd), name)
return _new_file(uintptr(fd), name, file_allocator())
}
_new_file :: proc(fd: uintptr, _: string = "") -> (f: ^File, err: Error) {
impl := new(File_Impl, file_allocator()) or_return
_new_file :: proc(fd: uintptr, _: string, allocator: runtime.Allocator) -> (f: ^File, err: Error) {
impl := new(File_Impl, allocator) or_return
defer if err != nil {
free(impl, file_allocator())
free(impl, allocator)
}
impl.file.impl = impl
impl.fd = linux.Fd(fd)
impl.allocator = file_allocator()
impl.name = _get_full_path(impl.fd, file_allocator()) or_return
impl.allocator = allocator
impl.name = _get_full_path(impl.fd, impl.allocator) or_return
impl.file.stream = {
data = impl,
procedure = _file_stream_proc,
@@ -272,28 +261,12 @@ _truncate :: proc(f: ^File, size: i64) -> Error {
}
_remove :: proc(name: string) -> Error {
is_dir_fd :: proc(fd: linux.Fd) -> bool {
s: linux.Stat
if linux.fstat(fd, &s) != .NONE {
return false
}
return linux.S_ISDIR(s.mode)
}
TEMP_ALLOCATOR_GUARD()
name_cstr := temp_cstring(name) or_return
fd, errno := linux.open(name_cstr, {.NOFOLLOW})
#partial switch (errno) {
case .ELOOP:
/* symlink */
case .NONE:
defer linux.close(fd)
if is_dir_fd(fd) {
return _get_platform_error(linux.rmdir(name_cstr))
}
case:
return _get_platform_error(errno)
if fd, errno := linux.open(name_cstr, _OPENDIR_FLAGS + {.NOFOLLOW}); errno == .NONE {
linux.close(fd)
return _get_platform_error(linux.rmdir(name_cstr))
}
return _get_platform_error(linux.unlink(name_cstr))
+29 -20
View File
@@ -21,23 +21,29 @@ File_Impl :: struct {
name: string,
cname: cstring,
fd: posix.FD,
allocator: runtime.Allocator,
}
@(init)
init_std_files :: proc() {
// NOTE: is this (paths) also the case on non darwin?
new_std :: proc(impl: ^File_Impl, fd: posix.FD, name: cstring) -> ^File {
impl.file.impl = impl
impl.fd = fd
impl.allocator = runtime.nil_allocator()
impl.cname = name
impl.name = string(name)
impl.file.stream = {
data = impl,
procedure = _file_stream_proc,
}
impl.file.fstat = _fstat
return &impl.file
}
stdin = __new_file(posix.STDIN_FILENO)
(^File_Impl)(stdin.impl).name = "/dev/stdin"
(^File_Impl)(stdin.impl).cname = "/dev/stdin"
stdout = __new_file(posix.STDIN_FILENO)
(^File_Impl)(stdout.impl).name = "/dev/stdout"
(^File_Impl)(stdout.impl).cname = "/dev/stdout"
stderr = __new_file(posix.STDIN_FILENO)
(^File_Impl)(stderr.impl).name = "/dev/stderr"
(^File_Impl)(stderr.impl).cname = "/dev/stderr"
@(static) files: [3]File_Impl
stdin = new_std(&files[0], posix.STDIN_FILENO, "/dev/stdin")
stdout = new_std(&files[1], posix.STDOUT_FILENO, "/dev/stdout")
stderr = new_std(&files[2], posix.STDERR_FILENO, "/dev/stderr")
}
_open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) {
@@ -72,10 +78,10 @@ _open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Err
return
}
return _new_file(uintptr(fd), name)
return _new_file(uintptr(fd), name, file_allocator())
}
_new_file :: proc(handle: uintptr, name: string) -> (f: ^File, err: Error) {
_new_file :: proc(handle: uintptr, name: string, allocator: runtime.Allocator) -> (f: ^File, err: Error) {
if name == "" {
err = .Invalid_Path
return
@@ -84,10 +90,10 @@ _new_file :: proc(handle: uintptr, name: string) -> (f: ^File, err: Error) {
return
}
crname := _posix_absolute_path(posix.FD(handle), name, file_allocator()) or_return
crname := _posix_absolute_path(posix.FD(handle), name, allocator) or_return
rname := string(crname)
f = __new_file(posix.FD(handle))
f = __new_file(posix.FD(handle), allocator)
impl := (^File_Impl)(f.impl)
impl.name = rname
impl.cname = crname
@@ -95,10 +101,11 @@ _new_file :: proc(handle: uintptr, name: string) -> (f: ^File, err: Error) {
return f, nil
}
__new_file :: proc(handle: posix.FD) -> ^File {
impl := new(File_Impl, file_allocator())
__new_file :: proc(handle: posix.FD, allocator: runtime.Allocator) -> ^File {
impl := new(File_Impl, allocator)
impl.file.impl = impl
impl.fd = posix.FD(handle)
impl.allocator = allocator
impl.file.stream = {
data = impl,
procedure = _file_stream_proc,
@@ -114,8 +121,10 @@ _close :: proc(f: ^File_Impl) -> (err: Error) {
err = _get_platform_error()
}
delete(f.cname, file_allocator())
free(f, file_allocator())
allocator := f.allocator
delete(f.cname, allocator)
free(f, allocator)
return
}
+37 -16
View File
@@ -44,17 +44,38 @@ File_Impl :: struct {
@(init)
init_std_files :: proc() {
stdin = new_file(uintptr(win32.GetStdHandle(win32.STD_INPUT_HANDLE)), "<stdin>")
stdout = new_file(uintptr(win32.GetStdHandle(win32.STD_OUTPUT_HANDLE)), "<stdout>")
stderr = new_file(uintptr(win32.GetStdHandle(win32.STD_ERROR_HANDLE)), "<stderr>")
}
@(fini)
fini_std_files :: proc() {
_destroy((^File_Impl)(stdin.impl))
_destroy((^File_Impl)(stdout.impl))
_destroy((^File_Impl)(stderr.impl))
}
new_std :: proc(impl: ^File_Impl, code: u32, name: string) -> ^File {
impl.file.impl = impl
impl.allocator = runtime.nil_allocator()
impl.fd = win32.GetStdHandle(code)
impl.name = name
impl.wname = nil
handle := _handle(&impl.file)
kind := File_Impl_Kind.File
if m: u32; win32.GetConsoleMode(handle, &m) {
kind = .Console
}
if win32.GetFileType(handle) == win32.FILE_TYPE_PIPE {
kind = .Pipe
}
impl.kind = kind
impl.file.stream = {
data = impl,
procedure = _file_stream_proc,
}
impl.file.fstat = _fstat
return &impl.file
}
@(static) files: [3]File_Impl
stdin = new_std(&files[0], win32.STD_INPUT_HANDLE, "<stdin>")
stdout = new_std(&files[1], win32.STD_OUTPUT_HANDLE, "<stdout>")
stderr = new_std(&files[2], win32.STD_ERROR_HANDLE, "<stderr>")
}
_handle :: proc(f: ^File) -> win32.HANDLE {
return win32.HANDLE(_fd(f))
@@ -132,21 +153,21 @@ _open_internal :: proc(name: string, flags: File_Flags, perm: int) -> (handle: u
_open :: proc(name: string, flags: File_Flags, perm: int) -> (f: ^File, err: Error) {
flags := flags if flags != nil else {.Read}
handle := _open_internal(name, flags, perm) or_return
return _new_file(handle, name)
return _new_file(handle, name, file_allocator())
}
_new_file :: proc(handle: uintptr, name: string) -> (f: ^File, err: Error) {
_new_file :: proc(handle: uintptr, name: string, allocator: runtime.Allocator) -> (f: ^File, err: Error) {
if handle == INVALID_HANDLE {
return
}
impl := new(File_Impl, file_allocator()) or_return
impl := new(File_Impl, allocator) or_return
defer if err != nil {
free(impl, file_allocator())
free(impl, allocator)
}
impl.file.impl = impl
impl.allocator = file_allocator()
impl.allocator = allocator
impl.fd = rawptr(handle)
impl.name = clone_string(name, impl.allocator) or_return
impl.wname = win32_utf8_to_wstring(name, impl.allocator) or_return
@@ -180,7 +201,7 @@ _open_buffered :: proc(name: string, buffer_size: uint, flags := File_Flags{.Rea
}
_new_file_buffered :: proc(handle: uintptr, name: string, buffer_size: uint) -> (f: ^File, err: Error) {
f, err = _new_file(handle, name)
f, err = _new_file(handle, name, file_allocator())
if f != nil && err == nil {
impl := (^File_Impl)(f.impl)
impl.r_buf = make([]byte, buffer_size, file_allocator())
+1 -1
View File
@@ -415,7 +415,7 @@ _region_resize :: proc(alloc: ^Allocation_Header, new_size: int, alloc_is_free_l
back_idx := -1
idx: u16
infinite: for {
for i := 0; i < len(region_iter.hdr.free_list); i += 1 {
for i := 0; i < int(region_iter.hdr.free_list_len); i += 1 {
idx = region_iter.hdr.free_list[i]
if _get_block_count(region_iter.memory[idx]) >= new_block_count {
break infinite
-2
View File
@@ -77,8 +77,6 @@ _mkdir_all :: proc(path: string, perm: int) -> Error {
}
_remove_all :: proc(path: string) -> Error {
DT_DIR :: 4
remove_all_dir :: proc(dfd: linux.Fd) -> Error {
n := 64
buf := make([]u8, n)
+2 -2
View File
@@ -10,8 +10,8 @@ _pipe :: proc() -> (r, w: ^File, err: Error) {
return nil, nil,_get_platform_error(errno)
}
r = _new_file(uintptr(fds[0])) or_return
w = _new_file(uintptr(fds[1])) or_return
r = _new_file(uintptr(fds[0]), "", file_allocator()) or_return
w = _new_file(uintptr(fds[1]), "", file_allocator()) or_return
return
}
+2 -2
View File
@@ -21,7 +21,7 @@ _pipe :: proc() -> (r, w: ^File, err: Error) {
return
}
r = __new_file(fds[0])
r = __new_file(fds[0], file_allocator())
ri := (^File_Impl)(r.impl)
rname := strings.builder_make(file_allocator())
@@ -31,7 +31,7 @@ _pipe :: proc() -> (r, w: ^File, err: Error) {
ri.name = strings.to_string(rname)
ri.cname = strings.to_cstring(&rname)
w = __new_file(fds[1])
w = __new_file(fds[1], file_allocator())
wi := (^File_Impl)(w.impl)
wname := strings.builder_make(file_allocator())
+10 -1
View File
@@ -290,12 +290,21 @@ process_open :: proc(pid: int, flags := Process_Open_Flags {}) -> (Process, Erro
return _process_open(pid, flags)
}
/*
OS-specific process attributes.
*/
Process_Attributes :: struct {
sys_attr: _Sys_Process_Attributes,
}
/*
The description of how a process should be created.
*/
Process_Desc :: struct {
// OS-specific attributes.
sys_attr: _Sys_Process_Attributes,
sys_attr: Process_Attributes,
// The working directory of the process. If the string has length 0, the
// working directory is assumed to be the current working directory of the
// current process.
+11 -36
View File
@@ -384,14 +384,6 @@ _Sys_Process_Attributes :: struct {}
@(private="package")
_process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
has_executable_permissions :: proc(fd: linux.Fd) -> bool {
backing: [48]u8
b := strings.builder_from_bytes(backing[:])
strings.write_string(&b, "/proc/self/fd/")
strings.write_int(&b, int(fd))
return linux.access(strings.to_cstring(&b), linux.X_OK) == .NONE
}
TEMP_ALLOCATOR_GUARD()
if len(desc.command) == 0 {
@@ -411,7 +403,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
}
// search PATH if just a plain name is provided
exe_fd: linux.Fd
exe_path: cstring
executable_name := desc.command[0]
if strings.index_byte(executable_name, '/') < 0 {
path_env := get_env("PATH", temp_allocator())
@@ -426,16 +418,11 @@ _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)
if exe_fd, errno = linux.openat(dir_fd, exe_path, {.PATH, .CLOEXEC}); errno != .NONE {
continue
exe_path = strings.to_cstring(&exe_builder)
if linux.access(exe_path, linux.X_OK) == .NONE {
found = true
break
}
if !has_executable_permissions(exe_fd) {
linux.close(exe_fd)
continue
}
found = true
break
}
if !found {
// check in cwd to match windows behavior
@@ -443,29 +430,18 @@ _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)
if exe_fd, errno = linux.openat(dir_fd, exe_path, {.PATH, .CLOEXEC}); errno != .NONE {
exe_path = strings.to_cstring(&exe_builder)
if linux.access(exe_path, linux.X_OK) != .NONE {
return process, .Not_Exist
}
if !has_executable_permissions(exe_fd) {
linux.close(exe_fd)
return process, .Permission_Denied
}
}
} else {
exe_path := temp_cstring(executable_name) or_return
if exe_fd, errno = linux.openat(dir_fd, exe_path, {.PATH, .CLOEXEC}); errno != .NONE {
return process, _get_platform_error(errno)
}
if !has_executable_permissions(exe_fd) {
linux.close(exe_fd)
return process, .Permission_Denied
exe_path = temp_cstring(executable_name) or_return
if linux.access(exe_path, linux.X_OK) != .NONE {
return process, .Not_Exist
}
}
// At this point, we have an executable.
defer linux.close(exe_fd)
// args and environment need to be a list of cstrings
// that are terminated by a nil pointer.
cargs := make([]cstring, len(desc.command) + 1, temp_allocator()) or_return
@@ -492,7 +468,6 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
}
defer linux.close(child_pipe_fds[READ])
// TODO: This is the traditional textbook implementation with fork.
// A more efficient implementation with vfork:
//
@@ -573,7 +548,7 @@ _process_start :: proc(desc: Process_Desc) -> (process: Process, err: Error) {
write_errno_to_parent_and_abort(child_pipe_fds[WRITE], errno)
}
errno = linux.execveat(exe_fd, "", &cargs[0], env, {.AT_EMPTY_PATH})
errno = linux.execveat(dir_fd, exe_path, &cargs[0], env)
assert(errno != nil)
write_errno_to_parent_and_abort(child_pipe_fds[WRITE], errno)
}