123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235 |
- //+private
- package os2
- import "core:strings"
- import "core:strconv"
- import "base:runtime"
- import "core:sys/unix"
- _Path_Separator :: '/'
- _Path_List_Separator :: ':'
- _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 == '/'
- }
- _mkdir :: proc(path: string, perm: File_Mode) -> Error {
- // NOTE: These modes would require sys_mknod, however, that would require
- // additional arguments to this function.
- if perm & (File_Mode_Named_Pipe | File_Mode_Device | File_Mode_Char_Device | File_Mode_Sym_Link) != 0 {
- return .Invalid_Argument
- }
- path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
- return _ok_or_error(unix.sys_mkdir(path_cstr, uint(perm & 0o777)))
- }
- _mkdir_all :: proc(path: string, perm: File_Mode) -> Error {
- _mkdirat :: proc(dfd: int, path: []u8, perm: int, has_created: ^bool) -> Error {
- if len(path) == 0 {
- return _ok_or_error(unix.sys_close(dfd))
- }
- i: int
- for /**/; i < len(path) - 1 && path[i] != '/'; i += 1 {}
- path[i] = 0
- new_dfd := unix.sys_openat(dfd, cstring(&path[0]), _OPENDIR_FLAGS)
- switch new_dfd {
- case -ENOENT:
- if res := unix.sys_mkdirat(dfd, cstring(&path[0]), uint(perm)); res < 0 {
- return _get_platform_error(res)
- }
- has_created^ = true
- if new_dfd = unix.sys_openat(dfd, cstring(&path[0]), _OPENDIR_FLAGS); new_dfd < 0 {
- return _get_platform_error(new_dfd)
- }
- fallthrough
- case 0:
- if res := unix.sys_close(dfd); res < 0 {
- return _get_platform_error(res)
- }
- // skip consecutive '/'
- for i += 1; i < len(path) && path[i] == '/'; i += 1 {}
- return _mkdirat(new_dfd, path[i:], perm, has_created)
- case:
- return _get_platform_error(new_dfd)
- }
- unreachable()
- }
- if perm & (File_Mode_Named_Pipe | File_Mode_Device | File_Mode_Char_Device | File_Mode_Sym_Link) != 0 {
- return .Invalid_Argument
- }
- // need something we can edit, and use to generate cstrings
- allocated: bool
- path_bytes: []u8
- if len(path) > _CSTRING_NAME_HEAP_THRESHOLD {
- allocated = true
- path_bytes = make([]u8, len(path) + 1)
- } else {
- path_bytes = make([]u8, len(path) + 1, context.temp_allocator)
- }
- // NULL terminate the byte slice to make it a valid cstring
- copy(path_bytes, path)
- path_bytes[len(path)] = 0
- dfd: int
- if path_bytes[0] == '/' {
- dfd = unix.sys_open("/", _OPENDIR_FLAGS)
- path_bytes = path_bytes[1:]
- } else {
- dfd = unix.sys_open(".", _OPENDIR_FLAGS)
- }
- if dfd < 0 {
- return _get_platform_error(dfd)
- }
-
- has_created: bool
- _mkdirat(dfd, path_bytes, int(perm & 0o777), &has_created) or_return
- if has_created {
- return nil
- }
- return .Exist
- //return has_created ? nil : .Exist
- }
- dirent64 :: struct {
- d_ino: u64,
- d_off: u64,
- d_reclen: u16,
- d_type: u8,
- d_name: [1]u8,
- }
- _remove_all :: proc(path: string) -> Error {
- DT_DIR :: 4
- _remove_all_dir :: proc(dfd: int) -> Error {
- n := 64
- buf := make([]u8, n)
- defer delete(buf)
- loop: for {
- getdents_res := unix.sys_getdents64(dfd, &buf[0], n)
- switch getdents_res {
- case -EINVAL:
- delete(buf)
- n *= 2
- buf = make([]u8, n)
- continue loop
- case -4096..<0:
- return _get_platform_error(getdents_res)
- case 0:
- break loop
- }
- d: ^dirent64
- for i := 0; i < getdents_res; i += int(d.d_reclen) {
- d = (^dirent64)(rawptr(&buf[i]))
- d_name_cstr := cstring(&d.d_name[0])
- buf_len := uintptr(d.d_reclen) - offset_of(d.d_name)
- /* check for current directory (.) */
- #no_bounds_check if buf_len > 1 && d.d_name[0] == '.' && d.d_name[1] == 0 {
- continue
- }
- /* check for parent directory (..) */
- #no_bounds_check if buf_len > 2 && d.d_name[0] == '.' && d.d_name[1] == '.' && d.d_name[2] == 0 {
- continue
- }
- unlink_res: int
- switch d.d_type {
- case DT_DIR:
- new_dfd := unix.sys_openat(dfd, d_name_cstr, _OPENDIR_FLAGS)
- if new_dfd < 0 {
- return _get_platform_error(new_dfd)
- }
- defer unix.sys_close(new_dfd)
- _remove_all_dir(new_dfd) or_return
- unlink_res = unix.sys_unlinkat(dfd, d_name_cstr, int(unix.AT_REMOVEDIR))
- case:
- unlink_res = unix.sys_unlinkat(dfd, d_name_cstr)
- }
- if unlink_res < 0 {
- return _get_platform_error(unlink_res)
- }
- }
- }
- return nil
- }
- path_cstr := strings.clone_to_cstring(path, context.temp_allocator)
- fd := unix.sys_open(path_cstr, _OPENDIR_FLAGS)
- switch fd {
- case -ENOTDIR:
- return _ok_or_error(unix.sys_unlink(path_cstr))
- case -4096..<0:
- return _get_platform_error(fd)
- }
- defer unix.sys_close(fd)
- _remove_all_dir(fd) or_return
- return _ok_or_error(unix.sys_rmdir(path_cstr))
- }
- _getwd :: proc(allocator: runtime.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.
- // NOTE(jason): Avoiding libc, so just use 4096 directly
- PATH_MAX :: 4096
- buf := make([dynamic]u8, PATH_MAX, allocator)
- for {
- #no_bounds_check res := unix.sys_getcwd(&buf[0], uint(len(buf)))
- if res >= 0 {
- return strings.string_from_null_terminated_ptr(&buf[0], len(buf)), nil
- }
- if res != -ERANGE {
- return "", _get_platform_error(res)
- }
- resize(&buf, len(buf)+PATH_MAX)
- }
- unreachable()
- }
- _setwd :: proc(dir: string) -> Error {
- dir_cstr := strings.clone_to_cstring(dir, context.temp_allocator)
- return _ok_or_error(unix.sys_chdir(dir_cstr))
- }
- _get_full_path :: proc(fd: int, allocator: runtime.Allocator) -> string {
- PROC_FD_PATH :: "/proc/self/fd/"
- buf: [32]u8
- copy(buf[:], PROC_FD_PATH)
- strconv.itoa(buf[len(PROC_FD_PATH):], fd)
- fullpath: string
- err: Error
- if fullpath, err = _read_link_cstr(cstring(&buf[0]), allocator); err != nil || fullpath[0] != '/' {
- return ""
- }
- return fullpath
- }
|