From 20fa012b570635d81e8cd52f6f8e47cedb8ecc03 Mon Sep 17 00:00:00 2001 From: John Alanbrook Date: Thu, 4 Dec 2025 14:19:02 -0600 Subject: [PATCH] add symlink capabilities to fd.c --- scripts/fd.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/scripts/fd.c b/scripts/fd.c index 6f38c583..7e068fe2 100644 --- a/scripts/fd.c +++ b/scripts/fd.c @@ -205,11 +205,6 @@ JSC_SCALL(fd_mkdir, ret = JS_ThrowInternalError(js, "could not make directory %s: %s", str, strerror(errno)); ) -JSC_SCALL(fd_unlink, - if (unlink(str) != 0) - ret = JS_ThrowInternalError(js, "could not remove file %s: %s", str, strerror(errno)); -) - JSC_SCALL(fd_mv, if (argc < 2) ret = JS_ThrowTypeError(js, "fd.mv requires 2 arguments: old path and new path"); @@ -245,6 +240,29 @@ JSC_SCALL(fd_symlink, } ) +JSC_SCALL(fd_unlink, +#ifdef _WIN32 + // On Windows, try to remove read-only attribute first + DWORD attrs = GetFileAttributesA(str); + if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_READONLY)) { + SetFileAttributesA(str, attrs & ~FILE_ATTRIBUTE_READONLY); + } + if (!DeleteFileA(str)) { + DWORD err = GetLastError(); + if (err == ERROR_ACCESS_DENIED) { + // Might be a directory, try RemoveDirectory + if (!RemoveDirectoryA(str)) + ret = JS_ThrowInternalError(js, "could not remove %s: error %lu", str, GetLastError()); + } else { + ret = JS_ThrowInternalError(js, "could not remove file %s: error %lu", str, err); + } + } +#else + if (unlink(str) != 0) + ret = JS_ThrowInternalError(js, "could not remove file %s: %s", str, strerror(errno)); +#endif +) + // Helper function for recursive removal static int remove_recursive(const char *path) { struct stat st; @@ -570,6 +588,40 @@ JSC_SCALL(fd_enumerate, ret = results; ) +JSC_CCALL(fd_realpath, + const char *path = JS_ToCString(js, argv[0]); + if (!path) return JS_EXCEPTION; + +#ifdef _WIN32 + char resolved[PATH_MAX]; + DWORD len = GetFullPathNameA(path, PATH_MAX, resolved, NULL); + JS_FreeCString(js, path); + if (len == 0 || len >= PATH_MAX) { + return JS_ThrowInternalError(js, "realpath failed: %s", strerror(errno)); + } + return JS_NewString(js, resolved); +#else + char *resolved = realpath(path, NULL); + JS_FreeCString(js, path); + if (!resolved) { + return JS_ThrowInternalError(js, "realpath failed: %s", strerror(errno)); + } + JSValue result = JS_NewString(js, resolved); + free(resolved); + return result; +#endif +) + +JSC_CCALL(fd_is_link, + const char *path = JS_ToCString(js, argv[0]); + if (!path) return JS_EXCEPTION; + + struct stat st; + if (lstat(path, &st) == 0 && S_ISLNK(st.st_mode)) + return JS_NewBool(js, 1); + return JS_NewBool(js, 0); +) + static const JSCFunctionListEntry js_fd_funcs[] = { MIST_FUNC_DEF(fd, open, 2), MIST_FUNC_DEF(fd, write, 2), @@ -590,8 +642,10 @@ static const JSCFunctionListEntry js_fd_funcs[] = { MIST_FUNC_DEF(fd, readdir, 1), MIST_FUNC_DEF(fd, is_file, 1), MIST_FUNC_DEF(fd, is_dir, 1), + MIST_FUNC_DEF(fd, is_link, 1), MIST_FUNC_DEF(fd, enumerate, 2), MIST_FUNC_DEF(fd, symlink, 2), + MIST_FUNC_DEF(fd, realpath, 1), }; JSValue js_fd_use(JSContext *js) {