diff options
| author | gingerBill <bill@gingerbill.org> | 2020-09-26 16:02:03 +0100 |
|---|---|---|
| committer | gingerBill <bill@gingerbill.org> | 2020-09-26 16:02:03 +0100 |
| commit | 840af6825aa5a728bb7fab3be24edff34aa3d571 (patch) | |
| tree | 209fb558042e30cf1ea3d090c3a14ca3f320326e | |
| parent | 3ccaf47566f31d22daf3493f8fdcaea4fa825748 (diff) | |
Update packages os, path, and filepath
| -rw-r--r-- | core/os/env_windows.odin | 103 | ||||
| -rw-r--r-- | core/os/file_windows.odin | 400 | ||||
| -rw-r--r-- | core/os/os_windows.odin | 193 | ||||
| -rw-r--r-- | core/path/filepath/path.odin | 160 | ||||
| -rw-r--r-- | core/path/filepath/path_unix.odin | 1 | ||||
| -rw-r--r-- | core/path/filepath/path_windows.odin | 106 | ||||
| -rw-r--r-- | core/path/path_unix.odin | 83 | ||||
| -rw-r--r-- | core/path/path_windows.odin | 124 | ||||
| -rw-r--r-- | core/runtime/default_allocators.odin | 2 | ||||
| -rw-r--r-- | core/sys/windows/kernel32.odin | 4 |
10 files changed, 765 insertions, 411 deletions
diff --git a/core/os/env_windows.odin b/core/os/env_windows.odin new file mode 100644 index 000000000..8a4cee967 --- /dev/null +++ b/core/os/env_windows.odin @@ -0,0 +1,103 @@ +package os + +import win32 "core:sys/windows" +import "core:mem" + +// lookup_env gets the value of the environment variable named by the key +// If the variable is found in the environment the value (which can be empty) is returned and the boolean is true +// Otherwise the returned value will be empty and the boolean will be false +// NOTE: the value will be allocated with the supplied allocator +lookup_env :: proc(key: string, allocator := context.allocator) -> (value: string, found: bool) { + if key == "" { + return; + } + wkey := win32.utf8_to_wstring(key); + b := make([dynamic]u16, 100, context.temp_allocator); + for { + n := win32.GetEnvironmentVariableW(wkey, raw_data(b), u32(len(b))); + if n == 0 { + err := win32.GetLastError(); + if err == u32(ERROR_ENVVAR_NOT_FOUND) { + return "", false; + } + } + + if n <= u32(len(b)) { + value = win32.utf16_to_utf8(b[:n], allocator); + found = true; + return; + } + + resize(&b, len(b)*2); + } +} + + +// get_env retrieves the value of the environment variable named by the key +// It returns the value, which will be empty if the variable is not present +// To distinguish between an empty value and an unset value, use lookup_env +// NOTE: the value will be allocated with the supplied allocator +get_env :: proc(key: string, allocator := context.allocator) -> (value: string) { + value, _ = lookup_env(key, allocator); + return; +} + +// set_env sets the value of the environment variable named by the key +set_env :: proc(key, value: string) -> Errno { + k := win32.utf8_to_wstring(key); + v := win32.utf8_to_wstring(value); + + if !win32.SetEnvironmentVariableW(k, v) { + return Errno(win32.GetLastError()); + } + return 0; +} + +// unset_env unsets a single environment variable +unset_env :: proc(key: string) -> Errno { + k := win32.utf8_to_wstring(key); + if !win32.SetEnvironmentVariableW(k, nil) { + return Errno(win32.GetLastError()); + } + return 0; +} + +// 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 +environ :: proc(allocator := context.allocator) -> []string { + envs := win32.GetEnvironmentStringsW(); + if envs == nil { + return nil; + } + defer win32.FreeEnvironmentStringsW(envs); + + r := make([dynamic]string, 0, 50, allocator); + for from, i, p := 0, 0, envs; true; i += 1 { + c := (^u16)(uintptr(p) + uintptr(i*2))^; + if c == 0 { + if i <= from { + break; + } + w := mem.slice_ptr(mem.ptr_offset(p, from), i-from); + + append(&r, win32.utf16_to_utf8(w, allocator)); + from = i + 1; + } + } + + return r[:]; +} + + +// clear_env deletes all environment variables +clear_env :: proc() { + envs := environ(context.temp_allocator); + for env in envs { + for j in 1..<len(env) { + if env[j] == '=' { + unset_env(env[0:j]); + break; + } + } + } +} diff --git a/core/os/file_windows.odin b/core/os/file_windows.odin new file mode 100644 index 000000000..bf9784319 --- /dev/null +++ b/core/os/file_windows.odin @@ -0,0 +1,400 @@ +package os + +import win32 "core:sys/windows" +import "intrinsics" + +is_path_separator :: proc(c: byte) -> bool { + return c == '/' || c == '\\'; +} + +open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) { + if len(path) == 0 { + return INVALID_HANDLE, ERROR_FILE_NOT_FOUND; + } + + access: u32; + switch mode & (O_RDONLY|O_WRONLY|O_RDWR) { + case O_RDONLY: access = win32.FILE_GENERIC_READ; + case O_WRONLY: access = win32.FILE_GENERIC_WRITE; + case O_RDWR: access = win32.FILE_GENERIC_READ | win32.FILE_GENERIC_WRITE; + } + + if mode&O_APPEND != 0 { + access &~= win32.FILE_GENERIC_WRITE; + access |= win32.FILE_APPEND_DATA; + } + if mode&O_CREATE != 0 { + access |= win32.FILE_GENERIC_WRITE; + } + + share_mode := u32(win32.FILE_SHARE_READ|win32.FILE_SHARE_WRITE); + sa: ^win32.SECURITY_ATTRIBUTES = nil; + sa_inherit := win32.SECURITY_ATTRIBUTES{nLength = size_of(win32.SECURITY_ATTRIBUTES), bInheritHandle = true}; + if mode&O_CLOEXEC == 0 { + sa = &sa_inherit; + } + + create_mode: u32; + switch { + case mode&(O_CREATE|O_EXCL) == (O_CREATE | O_EXCL): + create_mode = win32.CREATE_NEW; + case mode&(O_CREATE|O_TRUNC) == (O_CREATE | O_TRUNC): + create_mode = win32.CREATE_ALWAYS; + case mode&O_CREATE == O_CREATE: + create_mode = win32.OPEN_ALWAYS; + case mode&O_TRUNC == O_TRUNC: + create_mode = win32.TRUNCATE_EXISTING; + case: + create_mode = win32.OPEN_EXISTING; + } + wide_path := win32.utf8_to_wstring(path); + handle := Handle(win32.CreateFileW(auto_cast wide_path, access, share_mode, sa, create_mode, win32.FILE_ATTRIBUTE_NORMAL|win32.FILE_FLAG_BACKUP_SEMANTICS, nil)); + if handle != INVALID_HANDLE { + return handle, ERROR_NONE; + } + + err := Errno(win32.GetLastError()); + return INVALID_HANDLE, err; +} + +close :: proc(fd: Handle) -> Errno { + if !win32.CloseHandle(win32.HANDLE(fd)) { + return Errno(win32.GetLastError()); + } + return ERROR_NONE; +} + + + +write :: proc(fd: Handle, data: []byte) -> (int, Errno) { + if len(data) == 0 { + return 0, ERROR_NONE; + } + + single_write_length: win32.DWORD; + total_write: i64; + length := i64(len(data)); + + for total_write < length { + remaining := length - total_write; + MAX :: 1<<31-1; + to_write := win32.DWORD(min(i32(remaining), MAX)); + + e := win32.WriteFile(win32.HANDLE(fd), &data[total_write], to_write, &single_write_length, nil); + if single_write_length <= 0 || !e { + err := Errno(win32.GetLastError()); + return int(total_write), err; + } + total_write += i64(single_write_length); + } + return int(total_write), ERROR_NONE; +} + +read :: proc(fd: Handle, data: []byte) -> (int, Errno) { + if len(data) == 0 { + return 0, ERROR_NONE; + } + + single_read_length: win32.DWORD; + total_read: i64; + length := i64(len(data)); + + for total_read < length { + remaining := length - total_read; + MAX :: 1<<32-1; + to_read := win32.DWORD(min(u32(remaining), MAX)); + + e := win32.ReadFile(win32.HANDLE(fd), &data[total_read], to_read, &single_read_length, nil); + if single_read_length <= 0 || !e { + err := Errno(win32.GetLastError()); + return int(total_read), err; + } + total_read += i64(single_read_length); + } + return int(total_read), ERROR_NONE; +} + +seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { + w: u32; + switch whence { + case 0: w = win32.FILE_BEGIN; + case 1: w = win32.FILE_CURRENT; + case 2: w = win32.FILE_END; + } + hi := i32(offset>>32); + lo := i32(offset); + ft := win32.GetFileType(win32.HANDLE(fd)); + if ft == win32.FILE_TYPE_PIPE { + return 0, ERROR_FILE_IS_PIPE; + } + + dw_ptr := win32.SetFilePointer(win32.HANDLE(fd), lo, &hi, w); + if dw_ptr == win32.INVALID_SET_FILE_POINTER { + err := Errno(win32.GetLastError()); + return 0, err; + } + return i64(hi)<<32 + i64(dw_ptr), ERROR_NONE; +} + +file_size :: proc(fd: Handle) -> (i64, Errno) { + length: win32.LARGE_INTEGER; + err: Errno; + if !win32.GetFileSizeEx(win32.HANDLE(fd), &length) { + err = Errno(win32.GetLastError()); + } + return i64(length), err; +} + + +// NOTE(bill): Uses startup to initialize it +stdin := get_std_handle(uint(win32.STD_INPUT_HANDLE)); +stdout := get_std_handle(uint(win32.STD_OUTPUT_HANDLE)); +stderr := get_std_handle(uint(win32.STD_ERROR_HANDLE)); + + +get_std_handle :: proc "contextless" (h: uint) -> Handle { + fd := win32.GetStdHandle(win32.DWORD(h)); + when size_of(uintptr) == 8 { + win32.SetHandleInformation(fd, win32.HANDLE_FLAG_INHERIT, 0); + } + return Handle(fd); +} + + +exists :: proc(path: string) -> bool { + wpath := win32.utf8_to_wstring(path, context.temp_allocator); + attribs := win32.GetFileAttributesW(wpath); + + return i32(attribs) != win32.INVALID_FILE_ATTRIBUTES; +} + +is_file :: proc(path: string) -> bool { + wpath := win32.utf8_to_wstring(path, context.temp_allocator); + attribs := win32.GetFileAttributesW(wpath); + + if i32(attribs) != win32.INVALID_FILE_ATTRIBUTES { + return attribs & win32.FILE_ATTRIBUTE_DIRECTORY == win32.FILE_ATTRIBUTE_DIRECTORY; + } + return false; +} + +is_dir :: proc(path: string) -> bool { + wpath := win32.utf8_to_wstring(path, context.temp_allocator); + attribs := win32.GetFileAttributesW(wpath); + + if i32(attribs) != win32.INVALID_FILE_ATTRIBUTES { + return attribs & win32.FILE_ATTRIBUTE_DIRECTORY != win32.FILE_ATTRIBUTE_DIRECTORY; + } + return false; +} + +// NOTE(tetra): GetCurrentDirectory is not thread safe with SetCurrentDirectory and GetFullPathName; +// The current directory is stored as a global variable in the process. +@private cwd_gate := false; + +get_current_directory :: proc(allocator := context.allocator) -> string { + for intrinsics.atomic_xchg(&cwd_gate, true) {} + + sz_utf16 := win32.GetCurrentDirectoryW(0, nil); + dir_buf_wstr := make([]u16, sz_utf16, context.temp_allocator); // the first time, it _includes_ the NUL. + + sz_utf16 = win32.GetCurrentDirectoryW(win32.DWORD(len(dir_buf_wstr)), auto_cast &dir_buf_wstr[0]); + assert(int(sz_utf16)+1 == len(dir_buf_wstr)); // the second time, it _excludes_ the NUL. + + intrinsics.atomic_store(&cwd_gate, false); + + return win32.utf16_to_utf8(dir_buf_wstr, allocator); +} + +set_current_directory :: proc(path: string) -> (err: Errno) { + wstr := win32.utf8_to_wstring(path); + + for intrinsics.atomic_xchg(&cwd_gate, true) {} + defer intrinsics.atomic_store(&cwd_gate, false); + + res := win32.SetCurrentDirectoryW(auto_cast wstr); + if !res { + return Errno(win32.GetLastError()); + } + + return; +} + + + +change_directory :: proc(path: string) -> Errno { + wpath := win32.utf8_to_wstring(path, context.temp_allocator); + return Errno(win32.SetCurrentDirectoryW(wpath)); +} + +make_directory :: proc(path: string, mode: u32) -> Errno { + wpath := win32.utf8_to_wstring(path, context.temp_allocator); + return Errno(win32.CreateDirectoryW(wpath, nil)); +} + + +remove_directory :: proc(path: string) -> Errno { + wpath := win32.utf8_to_wstring(path, context.temp_allocator); + return Errno(win32.RemoveDirectoryW(wpath)); +} + + + +@(private) +is_abs :: proc(path: string) -> bool { + if len(path) > 0 && path[0] == '/' { + return true; + } + when ODIN_OS == "windows" { + if len(path) > 2 { + switch path[0] { + case 'A'..'Z', 'a'..'z': + return path[1] == ':' && is_path_separator(path[2]); + } + } + } + return false; +} + +@(private) +fix_long_path :: proc(path: string) -> string { + if len(path) < 248 { + return path; + } + + if len(path) >= 2 && path[:2] == `\\` { + return path; + } + if !is_abs(path) { + return path; + } + + prefix :: `\\?`; + + path_buf := make([]byte, len(prefix)+len(path)+len(`\`), context.temp_allocator); + copy(path_buf, prefix); + n := len(path); + r, w := 0, len(prefix); + for r < n { + switch { + case is_path_separator(path[r]): + r += 1; + case path[r] == '.' && (r+1 == n || is_path_separator(path[r+1])): + r += 1; + case r+1 < n && path[r] == '.' && path[r+1] == '.' && (r+2 == n || is_path_separator(path[r+2])): + return path; + case: + path_buf[w] = '\\'; + w += 1; + for ; r < n && !is_path_separator(path[r]); r += 1 { + path_buf[w] = path[r]; + w += 1; + } + } + } + + if w == len(`\\?\c:`) { + path_buf[w] = '\\'; + w += 1; + } + return string(path_buf[:w]); +} + + +link :: proc(old_name, new_name: string) -> Errno { + n := win32.utf8_to_wstring(fix_long_path(new_name)); + o := win32.utf8_to_wstring(fix_long_path(old_name)); + return Errno(win32.CreateHardLinkW(n, o, nil)); +} + +unlink :: proc(path: string) -> Errno { + wpath := win32.utf8_to_wstring(path, context.temp_allocator); + return Errno(win32.DeleteFileW(wpath)); +} + + + +rename :: proc(old_path, new_path: string) -> Errno { + from := win32.utf8_to_wstring(old_path, context.temp_allocator); + to := win32.utf8_to_wstring(new_path, context.temp_allocator); + return Errno(win32.MoveFileExW(from, to, win32.MOVEFILE_REPLACE_EXISTING)); +} + + +ftruncate :: proc(fd: Handle, length: i64) -> (err: Errno) { + curr_off, e := seek(fd, 0, 1); + if e != 0 { + return e; + } + defer seek(fd, curr_off, 0); + _, e = seek(fd, length, 0); + if e != 0 { + return e; + } + ok := win32.SetEndOfFile(win32.HANDLE(fd)); + if !ok { + return Errno(win32.GetLastError()); + } + return ERROR_NONE; +} + +truncate :: proc(path: string, length: i64) -> (err: Errno) { + fd: Handle; + fd, err = open(path, O_WRONLY|O_CREATE, 0o666); + if err != 0 { + return; + } + defer close(fd); + err = ftruncate(fd, length); + return; +} + + +remove :: proc(name: string) -> Errno { + p := win32.utf8_to_wstring(fix_long_path(name)); + err, err1: win32.DWORD; + if !win32.DeleteFileW(p) { + err = win32.GetLastError(); + } + if err == 0 { + return 0; + } + if !win32.RemoveDirectoryW(p) { + err1 = win32.GetLastError(); + } + if err1 == 0 { + return 0; + } + + if err != err1 { + a := win32.GetFileAttributesW(p); + if a == ~u32(0) { + err = win32.GetLastError(); + } else { + if a & win32.FILE_ATTRIBUTE_DIRECTORY != 0 { + err = err1; + } else if a & win32.FILE_ATTRIBUTE_READONLY != 0 { + if win32.SetFileAttributesW(p, a &~ win32.FILE_ATTRIBUTE_READONLY) { + err = 0; + if !win32.DeleteFileW(p) { + err = win32.GetLastError(); + } + } + } + } + } + + return Errno(err); +} + + +pipe :: proc() -> (r, w: Handle, err: Errno) { + sa: win32.SECURITY_ATTRIBUTES; + sa.nLength = size_of(win32.SECURITY_ATTRIBUTES); + sa.bInheritHandle = true; + if !win32.CreatePipe((^win32.HANDLE)(&r), (^win32.HANDLE)(&w), &sa, 0) { + err = Errno(win32.GetLastError()); + } + return; +} + diff --git a/core/os/os_windows.odin b/core/os/os_windows.odin index d918983c6..b8c171008 100644 --- a/core/os/os_windows.odin +++ b/core/os/os_windows.odin @@ -2,7 +2,6 @@ package os import win32 "core:sys/windows" -import "core:intrinsics" Handle :: distinct uintptr; File_Time :: distinct u64; @@ -63,164 +62,6 @@ ERROR_FILE_IS_NOT_DIR: Errno : 1<<29 + 1; args := _alloc_command_line_arguments(); -is_path_separator :: proc(r: rune) -> bool { - return r == '/' || r == '\\'; -} - -open :: proc(path: string, mode: int = O_RDONLY, perm: int = 0) -> (Handle, Errno) { - if len(path) == 0 { - return INVALID_HANDLE, ERROR_FILE_NOT_FOUND; - } - - access: u32; - switch mode & (O_RDONLY|O_WRONLY|O_RDWR) { - case O_RDONLY: access = win32.FILE_GENERIC_READ; - case O_WRONLY: access = win32.FILE_GENERIC_WRITE; - case O_RDWR: access = win32.FILE_GENERIC_READ | win32.FILE_GENERIC_WRITE; - } - - if mode&O_APPEND != 0 { - access &~= win32.FILE_GENERIC_WRITE; - access |= win32.FILE_APPEND_DATA; - } - if mode&O_CREATE != 0 { - access |= win32.FILE_GENERIC_WRITE; - } - - share_mode := u32(win32.FILE_SHARE_READ|win32.FILE_SHARE_WRITE); - sa: ^win32.SECURITY_ATTRIBUTES = nil; - sa_inherit := win32.SECURITY_ATTRIBUTES{nLength = size_of(win32.SECURITY_ATTRIBUTES), bInheritHandle = true}; - if mode&O_CLOEXEC == 0 { - sa = &sa_inherit; - } - - create_mode: u32; - switch { - case mode&(O_CREATE|O_EXCL) == (O_CREATE | O_EXCL): - create_mode = win32.CREATE_NEW; - case mode&(O_CREATE|O_TRUNC) == (O_CREATE | O_TRUNC): - create_mode = win32.CREATE_ALWAYS; - case mode&O_CREATE == O_CREATE: - create_mode = win32.OPEN_ALWAYS; - case mode&O_TRUNC == O_TRUNC: - create_mode = win32.TRUNCATE_EXISTING; - case: - create_mode = win32.OPEN_EXISTING; - } - wide_path := win32.utf8_to_wstring(path); - handle := Handle(win32.CreateFileW(auto_cast wide_path, access, share_mode, sa, create_mode, win32.FILE_ATTRIBUTE_NORMAL|win32.FILE_FLAG_BACKUP_SEMANTICS, nil)); - if handle != INVALID_HANDLE { - return handle, ERROR_NONE; - } - - err := Errno(win32.GetLastError()); - return INVALID_HANDLE, err; -} - -close :: proc(fd: Handle) -> Errno { - if !win32.CloseHandle(win32.HANDLE(fd)) { - return Errno(win32.GetLastError()); - } - return ERROR_NONE; -} - - -write :: proc(fd: Handle, data: []byte) -> (int, Errno) { - if len(data) == 0 { - return 0, ERROR_NONE; - } - - single_write_length: win32.DWORD; - total_write: i64; - length := i64(len(data)); - - for total_write < length { - remaining := length - total_write; - MAX :: 1<<31-1; - to_write := win32.DWORD(min(i32(remaining), MAX)); - - e := win32.WriteFile(win32.HANDLE(fd), &data[total_write], to_write, &single_write_length, nil); - if single_write_length <= 0 || !e { - err := Errno(win32.GetLastError()); - return int(total_write), err; - } - total_write += i64(single_write_length); - } - return int(total_write), ERROR_NONE; -} - -read :: proc(fd: Handle, data: []byte) -> (int, Errno) { - if len(data) == 0 { - return 0, ERROR_NONE; - } - - single_read_length: win32.DWORD; - total_read: i64; - length := i64(len(data)); - - for total_read < length { - remaining := length - total_read; - MAX :: 1<<32-1; - to_read := win32.DWORD(min(u32(remaining), MAX)); - - e := win32.ReadFile(win32.HANDLE(fd), &data[total_read], to_read, &single_read_length, nil); - if single_read_length <= 0 || !e { - err := Errno(win32.GetLastError()); - return int(total_read), err; - } - total_read += i64(single_read_length); - } - return int(total_read), ERROR_NONE; -} - -seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) { - w: u32; - switch whence { - case 0: w = win32.FILE_BEGIN; - case 1: w = win32.FILE_CURRENT; - case 2: w = win32.FILE_END; - } - hi := i32(offset>>32); - lo := i32(offset); - ft := win32.GetFileType(win32.HANDLE(fd)); - if ft == win32.FILE_TYPE_PIPE { - return 0, ERROR_FILE_IS_PIPE; - } - - dw_ptr := win32.SetFilePointer(win32.HANDLE(fd), lo, &hi, w); - if dw_ptr == win32.INVALID_SET_FILE_POINTER { - err := Errno(win32.GetLastError()); - return 0, err; - } - return i64(hi)<<32 + i64(dw_ptr), ERROR_NONE; -} - -file_size :: proc(fd: Handle) -> (i64, Errno) { - length: win32.LARGE_INTEGER; - err: Errno; - if !win32.GetFileSizeEx(win32.HANDLE(fd), &length) { - err = Errno(win32.GetLastError()); - } - return i64(length), err; -} - - - -// NOTE(bill): Uses startup to initialize it -stdin := get_std_handle(uint(win32.STD_INPUT_HANDLE)); -stdout := get_std_handle(uint(win32.STD_OUTPUT_HANDLE)); -stderr := get_std_handle(uint(win32.STD_ERROR_HANDLE)); - - -get_std_handle :: proc "contextless" (h: uint) -> Handle { - fd := win32.GetStdHandle(win32.DWORD(h)); - when size_of(uintptr) == 8 { - win32.SetHandleInformation(fd, win32.HANDLE_FLAG_INHERIT, 0); - } - return Handle(fd); -} - - @@ -286,40 +127,6 @@ get_page_size :: proc() -> int { -// NOTE(tetra): GetCurrentDirectory is not thread safe with SetCurrentDirectory and GetFullPathName; -// The current directory is stored as a global variable in the process. -@private cwd_gate := false; - -get_current_directory :: proc(allocator := context.allocator) -> string { - for intrinsics.atomic_xchg(&cwd_gate, true) {} - - sz_utf16 := win32.GetCurrentDirectoryW(0, nil); - dir_buf_wstr := make([]u16, sz_utf16, context.temp_allocator); // the first time, it _includes_ the NUL. - - sz_utf16 = win32.GetCurrentDirectoryW(win32.DWORD(len(dir_buf_wstr)), auto_cast &dir_buf_wstr[0]); - assert(int(sz_utf16)+1 == len(dir_buf_wstr)); // the second time, it _excludes_ the NUL. - - intrinsics.atomic_store(&cwd_gate, false); - - return win32.utf16_to_utf8(dir_buf_wstr, allocator); -} - -set_current_directory :: proc(path: string) -> (err: Errno) { - wstr := win32.utf8_to_wstring(path); - - for intrinsics.atomic_xchg(&cwd_gate, true) {} - defer intrinsics.atomic_store(&cwd_gate, false); - - res := win32.SetCurrentDirectoryW(auto_cast wstr); - if !res { - return Errno(win32.GetLastError()); - } - - return; -} - - - exit :: proc(code: int) -> ! { win32.ExitProcess(win32.DWORD(code)); } diff --git a/core/path/filepath/path.odin b/core/path/filepath/path.odin index c15ff110a..f2dbedf1f 100644 --- a/core/path/filepath/path.odin +++ b/core/path/filepath/path.odin @@ -42,6 +42,7 @@ volume_name_len :: proc(path: string) -> int { } } + // URL: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx if l := len(path); l >= 5 && is_slash(path[0]) && is_slash(path[1]) && !is_slash(path[2]) && path[2] != '.' { for n := 3; n < l-1; n += 1 { @@ -65,6 +66,31 @@ volume_name_len :: proc(path: string) -> int { return 0; } +base :: proc(path: string) -> string { + if path == "" { + return "."; + } + + path := path; + for len(path) > 0 && is_separator(path[len(path)-1]) { + path = path[:len(path)-1]; + } + + path = path[volume_name_len(path):]; + + i := len(path)-1; + for i >= 0 && !is_separator(path[i]) { + i -= 1; + } + if i >= 0 { + path = path[i+1:]; + } + if path == "" { + return SEPARATOR_STRING; + } + return path; +} + clean :: proc(path: string, allocator := context.allocator) -> string { context.allocator = allocator; @@ -76,7 +102,11 @@ clean :: proc(path: string, allocator := context.allocator) -> string { if path == "" { if vol_len > 1 && original_path[1] != ':' { - return from_slash(original_path); + s, ok := from_slash(original_path); + if !ok { + s = strings.clone(s); + } + return s; } return strings.concatenate({original_path, "."}); } @@ -134,19 +164,133 @@ clean :: proc(path: string, allocator := context.allocator) -> string { } s := lazy_buffer_string(out); - cleaned := from_slash(s); + cleaned, new_allocation := from_slash(s); + if new_allocation { + delete(s); + } return cleaned; } -from_slash :: proc(path: string, allocator := context.allocator) -> string { +from_slash :: proc(path: string, allocator := context.allocator) -> (new_path: string, new_allocation: bool) { if SEPARATOR == '/' { - return path; + return path, false; + } + return strings.replace_all(path, "/", SEPARATOR_STRING, allocator); +} + +to_slash :: proc(path: string, allocator := context.allocator) -> (new_path: string, new_allocation: bool) { + if SEPARATOR == '/' { + return path, false; + } + return strings.replace_all(path, SEPARATOR_STRING, "/", allocator); +} + +ext :: proc(path: string) -> string { + for i := len(path)-1; i >= 0 && !is_separator(path[i]); i -= 1 { + if path[i] == '.' { + return path[i:]; + } + } + return ""; +} + + +Relative_Error :: enum { + None, + + Cannot_Relate, +} + +rel :: proc(base_path, target_path: string, allocator := context.allocator) -> (string, Relative_Error) { + context.allocator = allocator; + base_vol, target_vol := volume_name(base_path), volume_name(target_path); + base, target := clean(base_path), clean(target_path); + + delete_target := true; + defer { + if delete_target { + delete(target); + } + delete(base); + } + + if strings.equal_fold(target, base) { + return strings.clone("."), .None; + } + + base = base[len(base_vol):]; + target = target[len(target_vol):]; + if base == "." { + base = ""; + } + + base_slashed := len(base) > 0 && base[0] == SEPARATOR; + target_slashed := len(target) > 0 && target[0] == SEPARATOR; + if base_slashed != target_slashed || !strings.equal_fold(base_vol, target_vol) { + return "", .Cannot_Relate; + } + + bl, tl := len(base), len(target); + b0, bi, t0, ti: int; + for { + for bi < bl && base[bi] != SEPARATOR { + bi += 1; + } + for ti < tl && target[ti] != SEPARATOR { + ti += 1; + } + if !strings.equal_fold(target[t0:ti], base[t0:ti]) { + break; + } + if bi < bl { + bi += 1; + } + if ti < tl { + ti += 1; + } + b0, t0 = bi, ti; + } + + if base[b0:bi] == ".." { + return "", .Cannot_Relate; + } + + if b0 != bl { + seps := strings.count(base[b0:bl], SEPARATOR_STRING); + size := 2 + seps*3; + if tl != t0 { + size += 1 + tl - t0; + } + buf := make([]byte, size); + n := copy(buf, ".."); + for i in 0..<seps { + buf[n] = SEPARATOR; + copy(buf[n+1:], ".."); + n += 3; + } + if t0 != tl { + buf[n] = SEPARATOR; + copy(buf[n+1:], target[t0:]); + } + return string(buf), .None; + } + + delete_target = false; + return target[t0:], .None; +} + +dir :: proc(path: string, allocator := context.allocator) -> string { + vol := volume_name(path); + i := len(path) - 1; + for i >= len(vol) && is_separator(path[i]) { + i -= 1; } - s, ok := strings.replace_all(path, "/", SEPARATOR_STRING, allocator); - if !ok { - s = strings.clone(s, allocator); + dir := clean(path[len(vol) : i+1], allocator); + defer delete(dir, allocator); + if dir == "." && len(vol) > 2 { + return strings.clone(vol); } - return s; + return strings.concatenate({vol, dir}); } diff --git a/core/path/filepath/path_unix.odin b/core/path/filepath/path_unix.odin index 7ffe6291e..c9d0e28f1 100644 --- a/core/path/filepath/path_unix.odin +++ b/core/path/filepath/path_unix.odin @@ -3,3 +3,4 @@ package filepath SEPARATOR :: '/'; SEPARATOR_STRING :: `/`; +LIST_SEPARATOR :: ':'; diff --git a/core/path/filepath/path_windows.odin b/core/path/filepath/path_windows.odin index 604859ca8..e413133f8 100644 --- a/core/path/filepath/path_windows.odin +++ b/core/path/filepath/path_windows.odin @@ -1,10 +1,12 @@ package filepath import "core:strings" +import "core:os" +import win32 "core:sys/windows" SEPARATOR :: '\\'; SEPARATOR_STRING :: `\`; - +LIST_SEPARATOR :: ';'; reserved_names := []string{ "CON", "PRN", "AUX", "NUL", @@ -28,6 +30,108 @@ is_UNC :: proc(path: string) -> bool { return volume_name_len(path) > 2; } + +is_abs :: proc(path: string) -> bool { + if is_reserved_name(path) { + return true; + } + l := volume_name_len(path); + if l == 0 { + return false; + } + + path := path[l:]; + if path == "" { + return false; + } + return is_slash(path[0]); +} + + +@(private) +full_path :: proc(name: string, allocator := context.allocator) -> (path: string, err: os.Errno) { + name := name; + if name == "" { + name = "."; + } + p := win32.utf8_to_utf16(name, context.temp_allocator); + defer delete(p); + buf := make([dynamic]u16, 100, allocator); + for { + n := win32.GetFullPathNameW(raw_data(p), u32(len(buf)), raw_data(buf), nil); + if n == 0 { + delete(buf); + return "", os.Errno(win32.GetLastError()); + } + if n <= u32(len(buf)) { + return win32.utf16_to_utf8(buf[:n]), os.ERROR_NONE; + } + resize(&buf, len(buf)*2); + } +} + + + + +abs :: proc(path: string, allocator := context.allocator) -> (string, bool) { + full_path, err := full_path(path, context.temp_allocator); + if err != 0 { + return "", false; + } + return clean(full_path, allocator), true; +} + +split_list :: proc(path: string, allocator := context.allocator) -> []string { + if path == "" { + return nil; + } + + start: int; + quote: bool; + + start, quote = 0, false; + count := 0; + + for i := 0; i < len(path); i += 1 { + c := path[i]; + switch { + case c == '"': + quote = !quote; + case c == LIST_SEPARATOR && !quote: + count += 1; + } + } + + start, quote = 0, false; + list := make([]string, count, allocator); + index := 0; + for i := 0; i < len(path); i += 1 { + c := path[i]; + switch { + case c == '"': + quote = !quote; + case c == LIST_SEPARATOR && !quote: + list[index] = path[start:i]; + index += 1; + start = i + 1; + } + } + assert(index == count); + + for s, i in list { + s, new := strings.replace_all(s, `"`, ``, allocator); + if !new { + s = strings.clone(s, allocator); + } + list[i] = s; + } + + return list; +} + + + + join :: proc(elems: ..string, allocator := context.allocator) -> string { for e, i in elems { if e != "" { diff --git a/core/path/path_unix.odin b/core/path/path_unix.odin deleted file mode 100644 index b380d8187..000000000 --- a/core/path/path_unix.odin +++ /dev/null @@ -1,83 +0,0 @@ -//+build linux, darwin, freebsd -package path - -foreign import libc "system:c" - -import "core:os" -import "core:strings" - - -MAX :: 4096; // @note(bp): apparently PATH_MAX is bullshit - -OS_SEPARATOR :: '/'; -OS_SEPARATOR_STRING :: "/"; - -OS_SEPARATORS :: `/`; -OS_SEPARATORS_ARRAY :: []string{`/`}; - -@(private) -null_term :: proc(str: string) -> string { - for c, i in str { - if c == '\x00' { - return str[:i]; - } - } - return str; -} - - -full :: proc(path: string, allocator := context.temp_allocator) -> string { - cpath := strings.clone_to_cstring(path, context.temp_allocator); - - foreign libc { - realpath :: proc(path: cstring, resolved_path: ^u8) -> cstring ---; - } - - buf := make([dynamic]u8, MAX, MAX, allocator); - - cstr := realpath(cpath, &buf[0]); - for cstr == nil && os.get_last_error() == int(os.ENAMETOOLONG) { - resize(&buf, len(buf) + MAX); - cstr = realpath(cpath, &buf[0]); - } - - return null_term(string(buf[:])); -} - -current :: proc(allocator := context.temp_allocator) -> string { - foreign libc{ - getcwd :: proc(buf: ^u8, size: int) -> cstring ---; - } - - buf := make([dynamic]u8, MAX, MAX, allocator); - - cstr := getcwd(&buf[0], len(buf)); - for cstr == nil && os.get_last_error() == int(os.ENAMETOOLONG) { - resize(&buf, len(buf) + MAX); - cstr = getcwd(&buf[0], len(buf)); - } - - return null_term(string(buf[:])); -} - - -exists :: proc(path: string) -> bool { - if _, err := os.stat(path); err != 0 { - return true; - } - return false; -} - -is_dir :: proc(path: string) -> bool { - if stat, err := os.stat(path); err != 0 { - return os.S_ISDIR(u32(stat.mode)); - } - return false; -} - -is_file :: proc(path: string) -> bool { - if stat, err := os.stat(path); err != 0 { - return os.S_ISREG(u32(stat.mode)); - } - return false; -} diff --git a/core/path/path_windows.odin b/core/path/path_windows.odin deleted file mode 100644 index bc0360880..000000000 --- a/core/path/path_windows.odin +++ /dev/null @@ -1,124 +0,0 @@ -package path
-
-import "core:strings"
-import win32 "core:sys/windows"
-
-
-OS_SEPARATOR :: '\\';
-OS_SEPARATOR_STRING :: "\\";
-
-OS_SEPARATORS :: `/\`;
-OS_SEPARATORS_ARRAY :: []string{`/`, `\`};
-
-@(private)
-null_term :: proc "contextless" (str: string) -> string {
- for c, i in str {
- if c == '\x00' {
- return str[:i];
- }
- }
- return str;
-}
-
-
-long :: proc(path: string, allocator := context.temp_allocator) -> string {
- c_path := win32.utf8_to_wstring(path, context.temp_allocator);
- length := win32.GetLongPathNameW(c_path, nil, 0);
-
- if length == 0 {
- return "";
- }
-
- buf := make([]u16, length, context.temp_allocator);
-
- win32.GetLongPathNameW(c_path, win32.LPCWSTR(&buf[0]), length);
-
- res := win32.utf16_to_utf8(buf[:length], allocator);
-
- return null_term(res);
-}
-
-short :: proc(path: string, allocator := context.temp_allocator) -> string {
- c_path := win32.utf8_to_wstring(path, context.temp_allocator);
- length := win32.GetShortPathNameW(c_path, nil, 0);
-
- if length == 0 {
- return "";
- }
-
- buf := make([]u16, length, context.temp_allocator);
-
- win32.GetShortPathNameW(c_path, win32.LPCWSTR(&buf[0]), length);
-
- res := win32.utf16_to_utf8(buf[:length], allocator);
-
- return null_term(res);
-}
-
-full :: proc(path: string, allocator := context.temp_allocator) -> string {
- c_path := win32.utf8_to_wstring(path, context.temp_allocator);
- length := win32.GetFullPathNameW(c_path, 0, nil, nil);
-
- if length == 0 {
- return "";
- }
-
- buf := make([]u16, length, context.temp_allocator);
-
- win32.GetFullPathNameW(c_path, length, win32.LPCWSTR(&buf[0]), nil);
-
- res := win32.utf16_to_utf8(buf[:length], allocator);
-
- return null_term(res);
-}
-
-current :: proc(allocator := context.temp_allocator) -> string {
- length := win32.GetCurrentDirectoryW(0, nil);
-
- if length == 0 {
- return "";
- }
-
- buf := make([]u16, length, context.temp_allocator);
-
- win32.GetCurrentDirectoryW(length, win32.LPCWSTR(&buf[0]));
-
- res := win32.utf16_to_utf8(buf[:length], allocator);
-
- return strings.trim_null(res);
-}
-
-
-exists :: proc(path: string) -> bool {
- c_path := win32.utf8_to_wstring(path, context.temp_allocator);
- attribs := win32.GetFileAttributesW(c_path);
-
- return i32(attribs) != win32.INVALID_FILE_ATTRIBUTES;
-}
-
-is_dir :: proc(path: string) -> bool {
- c_path := win32.utf8_to_wstring(path, context.temp_allocator);
- attribs := win32.GetFileAttributesW(c_path);
-
- return (i32(attribs) != win32.INVALID_FILE_ATTRIBUTES) && (attribs & win32.FILE_ATTRIBUTE_DIRECTORY == win32.FILE_ATTRIBUTE_DIRECTORY);
-}
-
-is_file :: proc(path: string) -> bool {
- c_path := win32.utf8_to_wstring(path, context.temp_allocator);
- attribs := win32.GetFileAttributesW(c_path);
-
- return (i32(attribs) != win32.INVALID_FILE_ATTRIBUTES) && (attribs & win32.FILE_ATTRIBUTE_DIRECTORY != win32.FILE_ATTRIBUTE_DIRECTORY);
-}
-
-
-drive :: proc(path: string, new := false, allocator := context.allocator) -> string {
- if len(path) >= 3 {
- letter := path[:2];
-
- if path[1] == ':' && (path[2] == '\\' || path[2] == '/') {
- return new ? strings.clone(path[:2], allocator) : path[:2];
- }
- }
-
- return "";
-}
diff --git a/core/runtime/default_allocators.odin b/core/runtime/default_allocators.odin index a132b7a4f..052bbc91f 100644 --- a/core/runtime/default_allocators.odin +++ b/core/runtime/default_allocators.odin @@ -110,9 +110,7 @@ default_temp_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode } last_ptr := rawptr(&allocator.data[allocator.prev_offset]); if old_memory == last_ptr { - full_size := allocator.curr_offset - allocator.prev_offset; allocator.curr_offset = allocator.prev_offset; - mem_zero(last_ptr, full_size); return nil; } else { #no_bounds_check start, end := &allocator.data[0], &allocator.data[allocator.curr_offset]; diff --git a/core/sys/windows/kernel32.odin b/core/sys/windows/kernel32.odin index 55244c943..672114230 100644 --- a/core/sys/windows/kernel32.odin +++ b/core/sys/windows/kernel32.odin @@ -275,4 +275,8 @@ foreign kernel32 { GetFinalPathNameByHandleW :: proc(hFile: HANDLE, lpszFilePath: LPCWSTR, cchFilePath: DWORD, dwFlags: DWORD) -> DWORD --- + + SetEndOfFile :: proc(hFile: HANDLE) -> BOOL --- + + CreatePipe :: proc(hReadPipe, hWritePipe: ^HANDLE, lpPipeAttributes: LPSECURITY_ATTRIBUTES, nSize: DWORD) -> BOOL --- } |