diff --git a/lib/std/io/os/fileinfo.c3 b/lib/std/io/os/fileinfo.c3 index ee9b1f696..ba56ebf13 100644 --- a/lib/std/io/os/fileinfo.c3 +++ b/lib/std/io/os/fileinfo.c3 @@ -103,6 +103,23 @@ fn bool native_is_dir(String path) $endif } +fn bool native_is_link(String path) +{ + $switch: + $case env::DARWIN || env::LINUX || env::ANDROID || env::BSD_FAMILY || env::EMSCRIPTEN: + @pool() + { + Stat stat; + return libc::lstat(path.zstr_tcopy(), &stat) == 0 && libc_S_ISTYPE(stat.st_mode, libc::S_IFLNK); + }; + $case env::WIN32: + Win32_FILE_ATTRIBUTE_DATA data; + return @ok(get_win32_file_attributes(path, &data)) && (data.dwFileAttributes & win32::FILE_ATTRIBUTE_REPARSE_POINT) != 0; + $default: + return false; + $endswitch +} + fn Time_t? native_get_modified_time(String path) => @pool() { $switch: diff --git a/lib/std/io/path.c3 b/lib/std/io/path.c3 index 93b8368d2..ba4d3242a 100644 --- a/lib/std/io/path.c3 +++ b/lib/std/io/path.c3 @@ -36,6 +36,7 @@ enum PathEnv : (PathSeparator separator) fn bool is_dir(Path path) => os::native_is_dir(path); fn bool is_file(Path path) => os::native_is_file(path); +fn bool is_link(Path path) => os::native_is_link(path); fn long? file_size(Path path) => os::native_file_size(path); fn bool exists(Path path) => os::native_file_or_dir_exists(path); fn Path? tcwd() => cwd(tmem) @inline; diff --git a/lib/std/libc/os/android.c3 b/lib/std/libc/os/android.c3 index 3af7e027b..97f484d60 100644 --- a/lib/std/libc/os/android.c3 +++ b/lib/std/libc/os/android.c3 @@ -57,6 +57,7 @@ struct Stat @feat(!X86_64) } extern fn CInt stat(ZString path, Stat* stat); +extern fn CInt lstat(ZString path, Stat* stat); extern fn CInt get_nprocs(); extern fn CInt get_nprocs_conf(); diff --git a/lib/std/libc/os/darwin.c3 b/lib/std/libc/os/darwin.c3 index e9ba071a7..6669f67db 100644 --- a/lib/std/libc/os/darwin.c3 +++ b/lib/std/libc/os/darwin.c3 @@ -42,6 +42,7 @@ struct Stat } extern fn int stat(ZString str, Stat* stat) @cname("stat64"); +extern fn int lstat(ZString str, Stat* stat) @cname("lstat64"); extern fn CInt sysctl(CInt *name, CUInt namelen, void *oldp, usz *oldlenp, void *newp, usz newlen); diff --git a/lib/std/libc/os/emscripten.c3 b/lib/std/libc/os/emscripten.c3 index ebeb8eff2..61b6bf942 100644 --- a/lib/std/libc/os/emscripten.c3 +++ b/lib/std/libc/os/emscripten.c3 @@ -30,4 +30,5 @@ struct Stat } extern fn CInt stat(ZString path, Stat* stat); +extern fn CInt lstat(ZString path, Stat* stat); extern fn CInt emscripten_num_logical_cores(); diff --git a/lib/std/libc/os/freebsd.c3 b/lib/std/libc/os/freebsd.c3 index c8a5a13d4..2438f20e5 100644 --- a/lib/std/libc/os/freebsd.c3 +++ b/lib/std/libc/os/freebsd.c3 @@ -58,6 +58,7 @@ struct Stat @feat(!X86_64) } extern fn CInt stat(ZString path, Stat* stat); +extern fn CInt lstat(ZString path, Stat* stat); extern fn CInt get_nprocs(); extern fn CInt get_nprocs_conf(); @@ -66,4 +67,4 @@ extern fn CInt sysctl(CInt *name, CUInt namelen, void *oldp, usz *oldlenp, void const SCHED_OTHER = 2; const SCHED_FIFO = 1; -const SCHED_RR = 3; \ No newline at end of file +const SCHED_RR = 3; diff --git a/lib/std/libc/os/linux.c3 b/lib/std/libc/os/linux.c3 index 5580346e3..5c0e212fb 100644 --- a/lib/std/libc/os/linux.c3 +++ b/lib/std/libc/os/linux.c3 @@ -57,6 +57,7 @@ struct Stat @feat(!X86_64) } extern fn CInt stat(ZString path, Stat* stat); +extern fn CInt lstat(ZString path, Stat* stat); extern fn CInt get_nprocs(); extern fn CInt get_nprocs_conf(); diff --git a/lib/std/libc/os/netbsd.c3 b/lib/std/libc/os/netbsd.c3 index 9103c8106..e5d0c787c 100644 --- a/lib/std/libc/os/netbsd.c3 +++ b/lib/std/libc/os/netbsd.c3 @@ -33,9 +33,10 @@ struct Stat } extern fn CInt stat(ZString path, Stat* stat) @cname("__stat50"); +extern fn CInt lstat(ZString path, Stat* stat) @cname("__lstat50"); extern fn CInt sysctl(CInt *name, CUInt namelen, void *oldp, usz *oldlenp, void *newp, usz newlen); const SCHED_OTHER = 0; const SCHED_FIFO = 1; -const SCHED_RR = 2; \ No newline at end of file +const SCHED_RR = 2; diff --git a/lib/std/libc/os/openbsd.c3 b/lib/std/libc/os/openbsd.c3 index 96bde0eae..8b5d87118 100644 --- a/lib/std/libc/os/openbsd.c3 +++ b/lib/std/libc/os/openbsd.c3 @@ -55,9 +55,10 @@ struct Stat @feat(!X86_64) } extern fn CInt stat(ZString path, Stat* stat); +extern fn CInt lstat(ZString path, Stat* stat); extern fn CInt sysctl(CInt *name, CUInt namelen, void *oldp, usz *oldlenp, void *newp, usz newlen); const SCHED_OTHER = 2; const SCHED_FIFO = 1; -const SCHED_RR = 3; \ No newline at end of file +const SCHED_RR = 3; diff --git a/releasenotes.md b/releasenotes.md index 378abbd54..f3232648f 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -32,6 +32,7 @@ - Add a `range::slice` macro. - Add `log::get_logger`. - `RefCounted` now correctly makes a difference between dealloc and free. +- `Path.is_link` added. ### Fixes - Vmem incorrectly handled reserve page sizes. diff --git a/test/unit/stdlib/io/fileinfo.c3 b/test/unit/stdlib/io/fileinfo.c3 index e955868cd..1774bad70 100644 --- a/test/unit/stdlib/io/fileinfo.c3 +++ b/test/unit/stdlib/io/fileinfo.c3 @@ -16,6 +16,13 @@ fn void test_native_is_dir() @feat(LINUX, DARWIN, ANDROID) assert(!os::native_is_dir("/dev/null")); } +fn void test_is_link() @feat(LINUX) +{ + assert(path::is_link((Path)"/proc/self/exe")); + assert(!path::is_link((Path)"/")); + assert(!path::is_link((Path)"/this/file/does/not/exist")); +} + fn void test_native_is_file() @feat(BSD) {