mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-06 23:58:50 +00:00
Merge pull request #541 from Tetralux/getcwd
Add os.get_current_directory / os.set_current_directory
This commit is contained in:
@@ -5,6 +5,7 @@ foreign import libc "system:c"
|
|||||||
|
|
||||||
import "core:runtime"
|
import "core:runtime"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
import "core:c"
|
||||||
|
|
||||||
OS :: "darwin";
|
OS :: "darwin";
|
||||||
|
|
||||||
@@ -263,6 +264,8 @@ X_OK :: 1; // Test for execute permission
|
|||||||
F_OK :: 0; // Test for file existance
|
F_OK :: 0; // Test for file existance
|
||||||
|
|
||||||
foreign libc {
|
foreign libc {
|
||||||
|
@(link_name="__error") __error :: proc() -> ^int ---;
|
||||||
|
|
||||||
@(link_name="open") _unix_open :: proc(path: cstring, flags: int, #c_vararg mode: ..any) -> Handle ---;
|
@(link_name="open") _unix_open :: proc(path: cstring, flags: int, #c_vararg mode: ..any) -> Handle ---;
|
||||||
@(link_name="close") _unix_close :: proc(handle: Handle) ---;
|
@(link_name="close") _unix_close :: proc(handle: Handle) ---;
|
||||||
@(link_name="read") _unix_read :: proc(handle: Handle, buffer: rawptr, count: int) -> int ---;
|
@(link_name="read") _unix_read :: proc(handle: Handle, buffer: rawptr, count: int) -> int ---;
|
||||||
@@ -278,6 +281,8 @@ foreign libc {
|
|||||||
@(link_name="free") _unix_free :: proc(ptr: rawptr) ---;
|
@(link_name="free") _unix_free :: proc(ptr: rawptr) ---;
|
||||||
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr ---;
|
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr ---;
|
||||||
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---;
|
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---;
|
||||||
|
@(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring ---;
|
||||||
|
@(link_name="chdir") _unix_chdir :: proc(buf: cstring) -> c.int ---;
|
||||||
|
|
||||||
@(link_name="exit") _unix_exit :: proc(status: int) ---;
|
@(link_name="exit") _unix_exit :: proc(status: int) ---;
|
||||||
}
|
}
|
||||||
@@ -289,6 +294,10 @@ foreign dl {
|
|||||||
@(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---;
|
@(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get_last_error :: proc() -> int {
|
||||||
|
return __error()^;
|
||||||
|
}
|
||||||
|
|
||||||
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
|
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
|
||||||
cstr := strings.clone_to_cstring(path);
|
cstr := strings.clone_to_cstring(path);
|
||||||
handle := _unix_open(cstr, flags, mode);
|
handle := _unix_open(cstr, flags, mode);
|
||||||
@@ -394,6 +403,30 @@ getenv :: proc(name: string) -> (string, bool) {
|
|||||||
return string(cstr), true;
|
return string(cstr), true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get_current_directory :: proc() -> string {
|
||||||
|
page_size := get_page_size(); // NOTE(tetra): See note in os_linux.odin/get_current_directory.
|
||||||
|
buf := make([dynamic]u8, page_size);
|
||||||
|
for {
|
||||||
|
cwd := _unix_getcwd(cstring(#no_bounds_check &buf[0]), c.size_t(len(buf)));
|
||||||
|
if cwd != nil {
|
||||||
|
return string(cwd);
|
||||||
|
}
|
||||||
|
if Errno(get_last_error()) != ERANGE {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
resize(&buf, len(buf)+page_size);
|
||||||
|
}
|
||||||
|
unreachable();
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
set_current_directory :: proc(path: string) -> (err: Errno) {
|
||||||
|
cstr := strings.clone_to_cstring(path, context.temp_allocator);
|
||||||
|
res := _unix_chdir(cstr);
|
||||||
|
if res == -1 do return Errno(get_last_error());
|
||||||
|
return ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
exit :: inline proc(code: int) -> ! {
|
exit :: inline proc(code: int) -> ! {
|
||||||
_unix_exit(code);
|
_unix_exit(code);
|
||||||
}
|
}
|
||||||
|
|||||||
+57
-26
@@ -5,6 +5,7 @@ foreign import libc "system:c"
|
|||||||
|
|
||||||
import "core:runtime"
|
import "core:runtime"
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
import "core:c"
|
||||||
|
|
||||||
OS :: "linux";
|
OS :: "linux";
|
||||||
|
|
||||||
@@ -41,6 +42,7 @@ ESPIPE: Errno : 29;
|
|||||||
EROFS: Errno : 30;
|
EROFS: Errno : 30;
|
||||||
EPIPE: Errno : 32;
|
EPIPE: Errno : 32;
|
||||||
|
|
||||||
|
ERANGE: Errno : 34; /* Result too large */
|
||||||
EDEADLK: Errno : 35; /* Resource deadlock would occur */
|
EDEADLK: Errno : 35; /* Resource deadlock would occur */
|
||||||
ENAMETOOLONG: Errno : 36; /* File name too long */
|
ENAMETOOLONG: Errno : 36; /* File name too long */
|
||||||
ENOLCK: Errno : 37; /* No record locks available */
|
ENOLCK: Errno : 37; /* No record locks available */
|
||||||
@@ -259,29 +261,31 @@ foreign libc {
|
|||||||
@(link_name="__errno_location") __errno_location :: proc() -> ^int ---;
|
@(link_name="__errno_location") __errno_location :: proc() -> ^int ---;
|
||||||
@(link_name="syscall") syscall :: proc(number: Syscall, #c_vararg args: ..any) -> int ---;
|
@(link_name="syscall") syscall :: proc(number: Syscall, #c_vararg args: ..any) -> int ---;
|
||||||
|
|
||||||
@(link_name="open") _unix_open :: proc(path: cstring, flags: int, mode: int) -> Handle ---;
|
@(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle ---;
|
||||||
@(link_name="close") _unix_close :: proc(fd: Handle) -> int ---;
|
@(link_name="close") _unix_close :: proc(fd: Handle) -> c.int ---;
|
||||||
@(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: int) -> int ---;
|
@(link_name="read") _unix_read :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---;
|
||||||
@(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: int) -> int ---;
|
@(link_name="write") _unix_write :: proc(fd: Handle, buf: rawptr, size: c.size_t) -> c.ssize_t ---;
|
||||||
@(link_name="lseek64") _unix_seek :: proc(fd: Handle, offset: i64, whence: i32) -> i64 ---;
|
@(link_name="lseek64") _unix_seek :: proc(fd: Handle, offset: i64, whence: c.int) -> i64 ---;
|
||||||
@(link_name="gettid") _unix_gettid :: proc() -> u64 ---;
|
@(link_name="gettid") _unix_gettid :: proc() -> u64 ---;
|
||||||
@(link_name="getpagesize") _unix_getpagesize :: proc() -> i32 ---;
|
@(link_name="getpagesize") _unix_getpagesize :: proc() -> c.int ---;
|
||||||
@(link_name="stat") _unix_stat :: proc(path: cstring, stat: ^Stat) -> int ---;
|
@(link_name="stat") _unix_stat :: proc(path: cstring, stat: ^Stat) -> c.int ---;
|
||||||
@(link_name="fstat") _unix_fstat :: proc(fd: Handle, stat: ^Stat) -> int ---;
|
@(link_name="fstat") _unix_fstat :: proc(fd: Handle, stat: ^Stat) -> c.int ---;
|
||||||
@(link_name="access") _unix_access :: proc(path: cstring, mask: int) -> int ---;
|
@(link_name="access") _unix_access :: proc(path: cstring, mask: c.int) -> c.int ---;
|
||||||
|
|
||||||
@(link_name="malloc") _unix_malloc :: proc(size: int) -> rawptr ---;
|
@(link_name="malloc") _unix_malloc :: proc(size: c.size_t) -> rawptr ---;
|
||||||
@(link_name="calloc") _unix_calloc :: proc(num, size: int) -> rawptr ---;
|
@(link_name="calloc") _unix_calloc :: proc(num, size: c.size_t) -> rawptr ---;
|
||||||
@(link_name="free") _unix_free :: proc(ptr: rawptr) ---;
|
@(link_name="free") _unix_free :: proc(ptr: rawptr) ---;
|
||||||
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: int) -> rawptr ---;
|
@(link_name="realloc") _unix_realloc :: proc(ptr: rawptr, size: c.size_t) -> rawptr ---;
|
||||||
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---;
|
@(link_name="getenv") _unix_getenv :: proc(cstring) -> cstring ---;
|
||||||
|
@(link_name="getcwd") _unix_getcwd :: proc(buf: cstring, len: c.size_t) -> cstring ---;
|
||||||
|
@(link_name="chdir") _unix_chdir :: proc(buf: cstring) -> c.int ---;
|
||||||
|
|
||||||
@(link_name="exit") _unix_exit :: proc(status: int) -> ! ---;
|
@(link_name="exit") _unix_exit :: proc(status: c.int) -> ! ---;
|
||||||
}
|
}
|
||||||
foreign dl {
|
foreign dl {
|
||||||
@(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: int) -> rawptr ---;
|
@(link_name="dlopen") _unix_dlopen :: proc(filename: cstring, flags: c.int) -> rawptr ---;
|
||||||
@(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr ---;
|
@(link_name="dlsym") _unix_dlsym :: proc(handle: rawptr, symbol: cstring) -> rawptr ---;
|
||||||
@(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> int ---;
|
@(link_name="dlclose") _unix_dlclose :: proc(handle: rawptr) -> c.int ---;
|
||||||
@(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---;
|
@(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,7 +299,7 @@ get_last_error :: proc() -> int {
|
|||||||
|
|
||||||
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
|
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
|
||||||
cstr := strings.clone_to_cstring(path);
|
cstr := strings.clone_to_cstring(path);
|
||||||
handle := _unix_open(cstr, flags, mode);
|
handle := _unix_open(cstr, c.int(flags), c.int(mode));
|
||||||
delete(cstr);
|
delete(cstr);
|
||||||
if handle == -1 {
|
if handle == -1 {
|
||||||
return INVALID_HANDLE, Errno(get_last_error());
|
return INVALID_HANDLE, Errno(get_last_error());
|
||||||
@@ -312,26 +316,26 @@ close :: proc(fd: Handle) -> Errno {
|
|||||||
}
|
}
|
||||||
|
|
||||||
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
read :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||||
bytes_read := _unix_read(fd, &data[0], len(data));
|
bytes_read := _unix_read(fd, &data[0], c.size_t(len(data)));
|
||||||
if bytes_read == -1 {
|
if bytes_read == -1 {
|
||||||
return -1, Errno(get_last_error());
|
return -1, Errno(get_last_error());
|
||||||
}
|
}
|
||||||
return bytes_read, ERROR_NONE;
|
return int(bytes_read), ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
write :: proc(fd: Handle, data: []byte) -> (int, Errno) {
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
return 0, ERROR_NONE;
|
return 0, ERROR_NONE;
|
||||||
}
|
}
|
||||||
bytes_written := _unix_write(fd, &data[0], len(data));
|
bytes_written := _unix_write(fd, &data[0], c.size_t(len(data)));
|
||||||
if bytes_written == -1 {
|
if bytes_written == -1 {
|
||||||
return -1, Errno(get_last_error());
|
return -1, Errno(get_last_error());
|
||||||
}
|
}
|
||||||
return bytes_written, ERROR_NONE;
|
return int(bytes_written), ERROR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
|
seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
|
||||||
res := _unix_seek(fd, offset, i32(whence));
|
res := _unix_seek(fd, offset, c.int(whence));
|
||||||
if res == -1 {
|
if res == -1 {
|
||||||
return -1, Errno(get_last_error());
|
return -1, Errno(get_last_error());
|
||||||
}
|
}
|
||||||
@@ -397,7 +401,7 @@ fstat :: inline proc(fd: Handle) -> (Stat, Errno) {
|
|||||||
access :: inline proc(path: string, mask: int) -> (bool, Errno) {
|
access :: inline proc(path: string, mask: int) -> (bool, Errno) {
|
||||||
cstr := strings.clone_to_cstring(path);
|
cstr := strings.clone_to_cstring(path);
|
||||||
defer delete(cstr);
|
defer delete(cstr);
|
||||||
result := _unix_access(cstr, mask);
|
result := _unix_access(cstr, c.int(mask));
|
||||||
if result == -1 {
|
if result == -1 {
|
||||||
return false, Errno(get_last_error());
|
return false, Errno(get_last_error());
|
||||||
}
|
}
|
||||||
@@ -406,11 +410,11 @@ access :: inline proc(path: string, mask: int) -> (bool, Errno) {
|
|||||||
|
|
||||||
heap_alloc :: proc(size: int) -> rawptr {
|
heap_alloc :: proc(size: int) -> rawptr {
|
||||||
assert(size >= 0);
|
assert(size >= 0);
|
||||||
return _unix_calloc(1, size);
|
return _unix_calloc(1, c.size_t(size));
|
||||||
}
|
}
|
||||||
|
|
||||||
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
heap_resize :: proc(ptr: rawptr, new_size: int) -> rawptr {
|
||||||
return _unix_realloc(ptr, new_size);
|
return _unix_realloc(ptr, c.size_t(new_size));
|
||||||
}
|
}
|
||||||
|
|
||||||
heap_free :: proc(ptr: rawptr) {
|
heap_free :: proc(ptr: rawptr) {
|
||||||
@@ -427,8 +431,35 @@ getenv :: proc(name: string) -> (string, bool) {
|
|||||||
return string(cstr), true;
|
return string(cstr), true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get_current_directory :: proc() -> string {
|
||||||
|
// 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.
|
||||||
|
page_size := get_page_size();
|
||||||
|
buf := make([dynamic]u8, page_size);
|
||||||
|
for {
|
||||||
|
cwd := _unix_getcwd(cstring(#no_bounds_check &buf[0]), c.size_t(len(buf)));
|
||||||
|
if cwd != nil {
|
||||||
|
return string(cwd);
|
||||||
|
}
|
||||||
|
if Errno(get_last_error()) != ERANGE {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
resize(&buf, len(buf)+page_size);
|
||||||
|
}
|
||||||
|
unreachable();
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
set_current_directory :: proc(path: string) -> (err: Errno) {
|
||||||
|
cstr := strings.clone_to_cstring(path, context.temp_allocator);
|
||||||
|
res := _unix_chdir(cstr);
|
||||||
|
if res == -1 do return Errno(get_last_error());
|
||||||
|
return ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
exit :: proc(code: int) -> ! {
|
exit :: proc(code: int) -> ! {
|
||||||
_unix_exit(code);
|
_unix_exit(c.int(code));
|
||||||
}
|
}
|
||||||
|
|
||||||
current_thread_id :: proc "contextless" () -> int {
|
current_thread_id :: proc "contextless" () -> int {
|
||||||
@@ -438,7 +469,7 @@ current_thread_id :: proc "contextless" () -> int {
|
|||||||
dlopen :: inline proc(filename: string, flags: int) -> rawptr {
|
dlopen :: inline proc(filename: string, flags: int) -> rawptr {
|
||||||
cstr := strings.clone_to_cstring(filename);
|
cstr := strings.clone_to_cstring(filename);
|
||||||
defer delete(cstr);
|
defer delete(cstr);
|
||||||
handle := _unix_dlopen(cstr, flags);
|
handle := _unix_dlopen(cstr, c.int(flags));
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
dlsym :: inline proc(handle: rawptr, symbol: string) -> rawptr {
|
dlsym :: inline proc(handle: rawptr, symbol: string) -> rawptr {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
package os
|
package os
|
||||||
|
|
||||||
import "core:sys/win32"
|
import "core:sys/win32"
|
||||||
|
import "core:intrinsics"
|
||||||
|
|
||||||
OS :: "windows";
|
OS :: "windows";
|
||||||
|
|
||||||
@@ -265,6 +266,40 @@ 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() -> string {
|
||||||
|
for intrinsics.atomic_xchg(&cwd_gate, true) {}
|
||||||
|
|
||||||
|
sz_utf16 := win32.get_current_directory_w(0, nil);
|
||||||
|
dir_buf_wstr := make([]u16, sz_utf16, context.temp_allocator); // the first time, it _includes_ the NUL.
|
||||||
|
|
||||||
|
sz_utf16 = win32.get_current_directory_w(u32(len(dir_buf_wstr)), cast(win32.Wstring) &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);
|
||||||
|
|
||||||
|
dir_utf8 := win32.utf16_to_utf8(dir_buf_wstr);
|
||||||
|
return dir_utf8[:len(dir_utf8)-1]; // NOTE(tetra): Remove the NUL.
|
||||||
|
}
|
||||||
|
|
||||||
|
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.set_current_directory_w(wstr);
|
||||||
|
if res == 0 do return Errno(win32.get_last_error());
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
exit :: proc(code: int) -> ! {
|
exit :: proc(code: int) -> ! {
|
||||||
win32.exit_process(u32(code));
|
win32.exit_process(u32(code));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,11 @@ foreign kernel32 {
|
|||||||
@(link_name="GetVersionExA") get_version :: proc(osvi: ^OS_Version_Info_Ex_A) ---;
|
@(link_name="GetVersionExA") get_version :: proc(osvi: ^OS_Version_Info_Ex_A) ---;
|
||||||
@(link_name="GetCurrentThreadId") get_current_thread_id :: proc() -> u32 ---;
|
@(link_name="GetCurrentThreadId") get_current_thread_id :: proc() -> u32 ---;
|
||||||
|
|
||||||
|
// NOTE(tetra): Not thread safe with SetCurrentDirectory and GetFullPathName;
|
||||||
|
// The current directory is stored as a global variable in the process.
|
||||||
|
@(link_name="GetCurrentDirectoryW") get_current_directory_w :: proc(len: u32, buf: Wstring) -> u32 ---;
|
||||||
|
@(link_name="SetCurrentDirectoryW") set_current_directory_w :: proc(buf: Wstring) -> u32 ---;
|
||||||
|
|
||||||
@(link_name="GetSystemTimeAsFileTime") get_system_time_as_file_time :: proc(system_time_as_file_time: ^Filetime) ---;
|
@(link_name="GetSystemTimeAsFileTime") get_system_time_as_file_time :: proc(system_time_as_file_time: ^Filetime) ---;
|
||||||
@(link_name="FileTimeToLocalFileTime") file_time_to_local_file_time :: proc(file_time: ^Filetime, local_file_time: ^Filetime) -> Bool ---;
|
@(link_name="FileTimeToLocalFileTime") file_time_to_local_file_time :: proc(file_time: ^Filetime, local_file_time: ^Filetime) -> Bool ---;
|
||||||
@(link_name="FileTimeToSystemTime") file_time_to_system_time :: proc(file_time: ^Filetime, system_time: ^Systemtime) -> Bool ---;
|
@(link_name="FileTimeToSystemTime") file_time_to_system_time :: proc(file_time: ^Filetime, system_time: ^Systemtime) -> Bool ---;
|
||||||
|
|||||||
Reference in New Issue
Block a user