mirror of
https://github.com/odin-lang/Odin.git
synced 2026-02-15 23:54:07 +00:00
commit to merge upstream/master
This commit is contained in:
@@ -11,35 +11,35 @@ _std_handle :: proc(kind: Std_Handle_Kind) -> Handle {
|
||||
return Handle(kind)
|
||||
}
|
||||
|
||||
__O_RDONLY :: 0o0
|
||||
__O_WRONLY :: 0o1
|
||||
__O_RDWR :: 0o2
|
||||
__O_CREAT :: 0o100
|
||||
__O_EXCL :: 0o200
|
||||
__O_TRUNC :: 0o1000
|
||||
__O_APPEND :: 0o2000
|
||||
__O_NONBLOCK :: 0o4000
|
||||
__O_LARGEFILE :: 0o100000
|
||||
__O_DIRECTORY :: 0o200000
|
||||
__O_SYNC :: 0o4010000
|
||||
__O_CLOEXEC :: 0o2000000
|
||||
_O_RDONLY :: 0o0
|
||||
_O_WRONLY :: 0o1
|
||||
_O_RDWR :: 0o2
|
||||
_O_CREAT :: 0o100
|
||||
_O_EXCL :: 0o200
|
||||
_O_TRUNC :: 0o1000
|
||||
_O_APPEND :: 0o2000
|
||||
_O_NONBLOCK :: 0o4000
|
||||
_O_LARGEFILE :: 0o100000
|
||||
_O_DIRECTORY :: 0o200000
|
||||
_O_SYNC :: 0o4010000
|
||||
_O_CLOEXEC :: 0o2000000
|
||||
|
||||
_open :: proc(name: string, flags: File_Flags, perm: File_Mode) -> (Handle, Error) {
|
||||
cstr := strings.clone_to_cstring(name, context.temp_allocator)
|
||||
|
||||
flags_i: int
|
||||
switch flags & O_RDONLY|O_WRONLY|O_RDWR {
|
||||
case O_RDONLY: flags_i = __O_RDONLY
|
||||
case O_WRONLY: flags_i = __O_WRONLY
|
||||
case O_RDWR: flags_i = __O_RDWR
|
||||
case O_RDONLY: flags_i = _O_RDONLY
|
||||
case O_WRONLY: flags_i = _O_WRONLY
|
||||
case O_RDWR: flags_i = _O_RDWR
|
||||
}
|
||||
|
||||
flags_i |= (__O_APPEND * int(.Append in flags))
|
||||
flags_i |= (__O_CREAT * int(.Create in flags))
|
||||
flags_i |= (__O_EXCL * int(.Excl in flags))
|
||||
flags_i |= (__O_SYNC * int(.Sync in flags))
|
||||
flags_i |= (__O_TRUNC * int(.Trunc in flags))
|
||||
flags_i |= (__O_CLOEXEC * int(.Close_On_Exec in flags))
|
||||
flags_i |= (_O_APPEND * int(.Append in flags))
|
||||
flags_i |= (_O_CREAT * int(.Create in flags))
|
||||
flags_i |= (_O_EXCL * int(.Excl in flags))
|
||||
flags_i |= (_O_SYNC * int(.Sync in flags))
|
||||
flags_i |= (_O_TRUNC * int(.Trunc in flags))
|
||||
flags_i |= (_O_CLOEXEC * int(.Close_On_Exec in flags))
|
||||
|
||||
handle_i := unix.sys_open(cstr, flags_i, int(perm))
|
||||
if handle_i < 0 {
|
||||
@@ -165,7 +165,6 @@ _remove :: proc(name: string) -> Error {
|
||||
}
|
||||
defer unix.sys_close(handle_i)
|
||||
|
||||
/* TODO: THIS WILL NOT WORK */
|
||||
if _is_dir(Handle(handle_i)) {
|
||||
return _ok_or_error(unix.sys_rmdir(name_cstr))
|
||||
}
|
||||
|
||||
@@ -8,7 +8,16 @@ import "core:path/filepath"
|
||||
_Path_Separator :: '/'
|
||||
_Path_List_Separator :: ':'
|
||||
|
||||
DIRECTORY_FLAGS :: __O_RDONLY|__O_NONBLOCK|__O_DIRECTORY|__O_LARGEFILE|__O_CLOEXEC
|
||||
_S_IFMT :: 0o170000 // Type of file mask
|
||||
_S_IFIFO :: 0o010000 // Named pipe (fifo)
|
||||
_S_IFCHR :: 0o020000 // Character special
|
||||
_S_IFDIR :: 0o040000 // Directory
|
||||
_S_IFBLK :: 0o060000 // Block special
|
||||
_S_IFREG :: 0o100000 // Regular
|
||||
_S_IFLNK :: 0o120000 // Symbolic link
|
||||
_S_IFSOCK :: 0o140000 // Socket
|
||||
|
||||
_OPENDIR_FLAGS :: _O_RDONLY|_O_NONBLOCK|_O_DIRECTORY|_O_LARGEFILE|_O_CLOEXEC
|
||||
|
||||
_is_path_separator :: proc(c: byte) -> bool {
|
||||
return c == '/'
|
||||
@@ -16,42 +25,17 @@ _is_path_separator :: proc(c: byte) -> bool {
|
||||
|
||||
_mkdir :: proc(path: string, perm: File_Mode) -> Error {
|
||||
path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
//TODO file_mode
|
||||
return _ok_or_error(unix.sys_mkdir(path_cstr))
|
||||
perm_i: int
|
||||
if perm & (File_Mode_Named_Pipe | File_Mode_Device | File_Mode_Char_Device | File_Mode_Sym_Link) != 0 {
|
||||
return .Invalid_Argument
|
||||
}
|
||||
|
||||
return _ok_or_error(unix.sys_mkdir(path_cstr, int(perm & 0o777)))
|
||||
}
|
||||
|
||||
// TODO
|
||||
_mkdir_all :: proc(path: string, perm: File_Mode) -> Error {
|
||||
_mkdir_all_stat :: proc(path: string, s: ^OS_Stat, perm: File_Mode) -> Error {
|
||||
if len(path) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
path := path[len(path)-1] == '/' ? path[:len(path)-1] : path
|
||||
dir, _ := filepath.split(path)
|
||||
|
||||
if len(dir) == 0 {
|
||||
return _mkdir(path, perm)
|
||||
}
|
||||
|
||||
dir_cstr := strings.clone_to_cstring(dir, context.temp_allocator)
|
||||
errno := int(unix.get_errno(unix.sys_stat(dir_cstr, s)))
|
||||
switch errno {
|
||||
case 0:
|
||||
if !S_ISDIR(s.mode) {
|
||||
return .Exist
|
||||
}
|
||||
return _mkdir(path, perm)
|
||||
case ENOENT:
|
||||
_mkdir_all_stat(dir, s, perm) or_return
|
||||
return _mkdir(path, perm)
|
||||
case:
|
||||
return _get_platform_error(errno)
|
||||
}
|
||||
unreachable()
|
||||
}
|
||||
// OS_Stat is fat. Make one and re-use it.
|
||||
s: OS_Stat = ---
|
||||
return _mkdir_all_stat(path, &s, perm)
|
||||
return nil
|
||||
}
|
||||
|
||||
dirent64 :: struct {
|
||||
@@ -62,17 +46,9 @@ dirent64 :: struct {
|
||||
d_name: [1]u8,
|
||||
}
|
||||
|
||||
DT_UNKNOWN :: 0
|
||||
DT_FIFO :: 1
|
||||
DT_CHR :: 2
|
||||
DT_DIR :: 4
|
||||
DT_BLK :: 6
|
||||
DT_REG :: 8
|
||||
DT_LNK :: 10
|
||||
DT_SOCK :: 12
|
||||
DT_WHT :: 14
|
||||
|
||||
_remove_all :: proc(path: string) -> Error {
|
||||
DT_DIR :: 4
|
||||
|
||||
_remove_all_dir :: proc(dfd: Handle) -> Error {
|
||||
n := 64
|
||||
buf := make([]u8, n)
|
||||
@@ -114,7 +90,7 @@ _remove_all :: proc(path: string) -> Error {
|
||||
|
||||
switch d.d_type {
|
||||
case DT_DIR:
|
||||
handle_i := unix.sys_openat(int(dfd), d_name_cstr, DIRECTORY_FLAGS)
|
||||
handle_i := unix.sys_openat(int(dfd), d_name_cstr, _OPENDIR_FLAGS)
|
||||
if handle_i < 0 {
|
||||
return _get_platform_error(handle_i)
|
||||
}
|
||||
@@ -135,7 +111,7 @@ _remove_all :: proc(path: string) -> Error {
|
||||
|
||||
cstr := strings.clone_to_cstring(path, context.temp_allocator)
|
||||
|
||||
handle_i := unix.sys_open(cstr, DIRECTORY_FLAGS)
|
||||
handle_i := unix.sys_open(cstr, _OPENDIR_FLAGS)
|
||||
switch handle_i {
|
||||
case -ENOTDIR:
|
||||
return _ok_or_error(unix.sys_unlink(cstr))
|
||||
@@ -149,7 +125,7 @@ _remove_all :: proc(path: string) -> Error {
|
||||
return _ok_or_error(unix.sys_rmdir(cstr))
|
||||
}
|
||||
|
||||
_getwd :: proc(allocator := context.allocator) -> (dir: string, err: Error) {
|
||||
_getwd :: proc(allocator := context.allocator) -> (string, Error) {
|
||||
// NOTE(tetra): I would use PATH_MAX here, but I was not able to find
|
||||
// an authoritative value for it across all systems.
|
||||
// The largest value I could find was 4096, so might as well use the page size.
|
||||
@@ -170,7 +146,7 @@ _getwd :: proc(allocator := context.allocator) -> (dir: string, err: Error) {
|
||||
unreachable()
|
||||
}
|
||||
|
||||
_setwd :: proc(dir: string) -> (err: Error) {
|
||||
_setwd :: proc(dir: string) -> Error {
|
||||
dir_cstr := strings.clone_to_cstring(dir, context.temp_allocator)
|
||||
return _ok_or_error(unix.sys_chdir(dir_cstr))
|
||||
}
|
||||
|
||||
@@ -1717,7 +1717,7 @@ sys_rmdir :: proc(path: cstring) -> int {
|
||||
}
|
||||
}
|
||||
|
||||
sys_mkdir :: proc(path: cstring, mode: u32 = 0o775) -> int {
|
||||
sys_mkdir :: proc(path: cstring, mode: int) -> int {
|
||||
when ODIN_ARCH != .arm64 {
|
||||
return int(intrinsics.syscall(SYS_mkdir, uintptr(rawptr(path)), uintptr(mode)))
|
||||
} else { // NOTE: arm64 does not have mkdir
|
||||
@@ -1725,6 +1725,14 @@ sys_mkdir :: proc(path: cstring, mode: u32 = 0o775) -> int {
|
||||
}
|
||||
}
|
||||
|
||||
sys_mknod :: proc(path: cstring, mode: int, dev: int) -> int {
|
||||
when ODIN_ARCH != .arm64 {
|
||||
return int(intrinsics.syscall(SYS_mknod, uintptr(rawptr(path)), uintptr(mode), uintptr(dev)))
|
||||
} else { // NOTE: arm64 does not have mknod
|
||||
return int(intrinsics.syscall(SYS_mknodat, uintptr(AT_FDCWD), uintptr(rawptr(path)), uintptr(mode), uintptr(dev)))
|
||||
}
|
||||
}
|
||||
|
||||
sys_truncate :: proc(path: cstring, length: i64) -> int {
|
||||
when ODIN_ARCH == .amd64 || ODIN_ARCH == .arm64 {
|
||||
return int(intrinsics.syscall(SYS_truncate, uintptr(rawptr(path)), uintptr(length)))
|
||||
|
||||
Binary file not shown.
@@ -1,5 +1,6 @@
|
||||
package test_os2
|
||||
|
||||
import "core:os"
|
||||
import "core:fmt"
|
||||
import "core:os/os2"
|
||||
import "core:testing"
|
||||
@@ -22,7 +23,7 @@ when ODIN_TEST {
|
||||
TEST_count += 1
|
||||
ok := value == expected
|
||||
if !ok {
|
||||
fmt.printf("expected %v, got %v", expected, value)
|
||||
fmt.printf("expected %v, got %v\n", expected, value)
|
||||
TEST_fail += 1
|
||||
return
|
||||
}
|
||||
@@ -45,12 +46,13 @@ when ODIN_TEST {
|
||||
}
|
||||
}
|
||||
|
||||
main :: proc()
|
||||
main :: proc()
|
||||
{
|
||||
t: testing.T
|
||||
file_test(&t)
|
||||
path_test(&t)
|
||||
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
|
||||
os.exit(TEST_fail > 0 ? 1 : 0)
|
||||
}
|
||||
|
||||
@private
|
||||
@@ -59,7 +61,7 @@ _expect_no_error :: proc(t: ^testing.T, e: os2.Error, loc := #caller_location) {
|
||||
}
|
||||
|
||||
|
||||
F_OK :: 0 // Test for file existance
|
||||
F_OK :: 0 // Test for file existence
|
||||
X_OK :: 1 // Test for execute permission
|
||||
W_OK :: 2 // Test for write permission
|
||||
R_OK :: 4 // Test for read permission
|
||||
@@ -84,44 +86,92 @@ file_test :: proc(t: ^testing.T) {
|
||||
expect(t, err != nil, "missing error")
|
||||
expect_value(t, fd, os2.INVALID_HANDLE)
|
||||
|
||||
fd, err = os2.open("write.txt", {.Write, .Create, .Trunc}, 0o664)
|
||||
// NOTE: no executable permissions here
|
||||
fd, err = os2.open("file.txt", {.Write, .Create, .Trunc}, 0o664)
|
||||
_expect_no_error(t, err)
|
||||
expect(t, fd != os2.INVALID_HANDLE, "unexpected handle")
|
||||
|
||||
s1 := "hello"
|
||||
b1 := transmute([]u8)s1
|
||||
|
||||
s := "hello"
|
||||
n: int
|
||||
n, err = os2.write_at(fd, b1, 10)
|
||||
n, err = os2.write_at(fd, transmute([]u8)s, 10)
|
||||
_expect_no_error(t, err)
|
||||
expect_value(t, n, 5)
|
||||
|
||||
s2 := "abcdefghij"
|
||||
b2 := transmute([]u8)s2
|
||||
|
||||
n, err = os2.write(fd, b2)
|
||||
s = "abcdefghij"
|
||||
n, err = os2.write(fd, transmute([]u8)s)
|
||||
_expect_no_error(t, err)
|
||||
expect_value(t, n, 10)
|
||||
|
||||
// seek to the "ll" in "hello"
|
||||
n64: i64
|
||||
n64, err = os2.seek(fd, 12, .Start)
|
||||
_expect_no_error(t, err)
|
||||
expect_value(t, n64, 12)
|
||||
|
||||
s = "11"
|
||||
n, err = os2.write(fd, transmute([]u8)s)
|
||||
_expect_no_error(t, err)
|
||||
expect_value(t, n, 2)
|
||||
|
||||
// seek to the "e" in "he11o"
|
||||
n64, err = os2.seek(fd, -3, .Current)
|
||||
_expect_no_error(t, err)
|
||||
expect_value(t, n64, 11)
|
||||
|
||||
s = "3"
|
||||
n, err = os2.write(fd, transmute([]u8)s)
|
||||
_expect_no_error(t, err)
|
||||
expect_value(t, n, 1)
|
||||
|
||||
// seek to the "o" in "h311o"
|
||||
n64, err = os2.seek(fd, -1, .End)
|
||||
_expect_no_error(t, err)
|
||||
expect_value(t, n64, 14)
|
||||
|
||||
s = "0"
|
||||
n, err = os2.write(fd, transmute([]u8)s)
|
||||
_expect_no_error(t, err)
|
||||
expect_value(t, n, 1)
|
||||
|
||||
_expect_no_error(t, os2.sync(fd))
|
||||
|
||||
// Add executable permissions to current file (as well as read/write to all)
|
||||
err = os2.chmod(fd, 0o766)
|
||||
_expect_no_error(t, err)
|
||||
|
||||
when ODIN_OS == .Linux {
|
||||
expect(t, unix.sys_access("file.txt", X_OK) == 0, "expected exec permission")
|
||||
}
|
||||
|
||||
// NOTE: chown not possible without root user
|
||||
//_expect_no_error(t, os2.chown(fd, 0, 0))
|
||||
_expect_no_error(t, os2.close(fd))
|
||||
|
||||
fd, err = os2.open("write.txt")
|
||||
|
||||
fd, err = os2.open("file.txt")
|
||||
_expect_no_error(t, err)
|
||||
|
||||
buf: [32]u8
|
||||
|
||||
|
||||
n, err = os2.read(fd, buf[:])
|
||||
_expect_no_error(t, err)
|
||||
expect_value(t, n, 15)
|
||||
expect_value(t, string(buf[:n]), "abcdefghijhello")
|
||||
expect_value(t, string(buf[:n]), "abcdefghijh3110")
|
||||
|
||||
n, err = os2.read_at(fd, buf[0:2], 1)
|
||||
_expect_no_error(t, err)
|
||||
expect_value(t, n, 2)
|
||||
expect_value(t, string(buf[0:2]), "bc")
|
||||
|
||||
n64, err = os2.file_size(fd)
|
||||
_expect_no_error(t, err)
|
||||
expect_value(t, n64, 15)
|
||||
|
||||
_expect_no_error(t, os2.close(fd))
|
||||
|
||||
_expect_no_error(t, os2.remove("file.txt"))
|
||||
_expect_no_error(t, os2.mkdir("empty dir", 0))
|
||||
_expect_no_error(t, os2.remove("empty dir"))
|
||||
}
|
||||
|
||||
@test
|
||||
@@ -131,7 +181,7 @@ path_test :: proc(t: ^testing.T) {
|
||||
err = os2.remove_all("a")
|
||||
_expect_no_error(t, err)
|
||||
}
|
||||
|
||||
|
||||
err = os2.mkdir_all("a/b/c/d", 0)
|
||||
_expect_no_error(t, err)
|
||||
|
||||
@@ -141,23 +191,25 @@ path_test :: proc(t: ^testing.T) {
|
||||
fd, err = os2.create("a/b/c/file.txt", 0o644)
|
||||
_expect_no_error(t, err)
|
||||
|
||||
err = os2.close(fd)
|
||||
_expect_no_error(t, err)
|
||||
|
||||
when ODIN_OS == .Linux {
|
||||
expect(t, unix.sys_access("a/b/c/file.txt", X_OK) < 0, "unexpected exec permission")
|
||||
} else {
|
||||
expect(t, os2.exists("a/b/c/file.txt"), "file does not exist")
|
||||
}
|
||||
|
||||
err = os2.rename("a/b/c/file.txt", "a/b/file.txt")
|
||||
_expect_no_error(t, err)
|
||||
|
||||
when ODIN_OS == .Linux {
|
||||
expect(t, unix.sys_access("a/b/c/file.txt", F_OK) < 0, "unexpected exec permission")
|
||||
expect(t, unix.sys_access("a/b/c/file.txt", F_OK) < 0, "unexpected file existence")
|
||||
} else {
|
||||
expect(t, !os2.exists("a/b/c/file.txt"), "unexpected file existence")
|
||||
}
|
||||
|
||||
err = os2.symlink("b/c/d", "a/symlink_to_d")
|
||||
_expect_no_error(t, err)
|
||||
|
||||
|
||||
symlink: string
|
||||
symlink, err = os2.read_link("a/symlink_to_d")
|
||||
_expect_no_error(t, err)
|
||||
@@ -171,8 +223,10 @@ path_test :: proc(t: ^testing.T) {
|
||||
|
||||
when ODIN_OS == .Linux {
|
||||
expect_value(t, unix.sys_access("a/b/c/d/shnt.txt", X_OK | R_OK | W_OK), 0)
|
||||
} else {
|
||||
expect(t, os2.exists("a/b/c/d/shnt.txt"), "file does not exist")
|
||||
}
|
||||
|
||||
|
||||
err = os2.remove_all("a")
|
||||
_expect_no_error(t, err)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user