#include "cell.h" #include #include #include #include #include #include #include #ifdef _WIN32 #include #include #include #define mkdir(path, mode) _mkdir(path) #define rmdir _rmdir #define unlink _unlink #define getcwd _getcwd #define PATH_MAX _MAX_PATH #define fsync(fd) _commit(fd) #define S_ISLNK(m) 0 #define S_ISSOCK(m) 0 #else #include #include #include #endif // Helper to convert JS value to file descriptor static int js2fd(JSContext *ctx, JSValueConst val) { int fd; if (JS_ToInt32(ctx, &fd, val) < 0) { JS_ThrowTypeError(ctx, "Expected file descriptor number"); return -1; } return fd; } // Helper function for writing static ssize_t js_fd_write_helper(JSContext *js, int fd, JSValue val) { } // POSIX FILE DESCRIPTOR FUNCTIONS JSC_SCALL(fd_open, int flags = O_RDWR | O_CREAT; mode_t mode = 0644; // Parse optional flags argument if (argc > 1 && JS_IsString(argv[1])) { const char *flag_str = JS_ToCString(js, argv[1]); flags = 0; if (strchr(flag_str, 'r')) flags |= O_RDONLY; if (strchr(flag_str, 'w')) flags |= O_WRONLY | O_CREAT | O_TRUNC; if (strchr(flag_str, 'a')) flags |= O_WRONLY | O_CREAT | O_APPEND; if (strchr(flag_str, '+')) { flags &= ~(O_RDONLY | O_WRONLY); flags |= O_RDWR; } JS_FreeCString(js, flag_str); } int fd = open(str, flags, mode); if (fd < 0) ret = JS_ThrowInternalError(js, "open failed: %s", strerror(errno)); else ret = JS_NewInt32(js, fd); ) JSC_CCALL(fd_write, int fd = js2fd(js, argv[0]); if (fd < 0) return JS_EXCEPTION; size_t len; ssize_t wrote; if (JS_IsString(argv[1])) { const char *data = JS_ToCStringLen(js, &len, argv[1]); if (!data) return JS_EXCEPTION; wrote = write(fd, data, len); JS_FreeCString(js, data); } else { void *data = js_get_blob_data(js, &len, argv[1]); if (data == -1) return JS_EXCEPTION; wrote = write(fd, data, len); } return JS_NewInt64(js, wrote); ) JSC_CCALL(fd_read, int fd = js2fd(js, argv[0]); if (fd < 0) return JS_EXCEPTION; size_t size = 4096; if (argc > 1) size = js2number(js, argv[1]); void *buf = malloc(size); if (!buf) return JS_ThrowInternalError(js, "malloc failed"); ssize_t bytes_read = read(fd, buf, size); if (bytes_read < 0) { free(buf); return JS_ThrowInternalError(js, "read failed: %s", strerror(errno)); } ret = js_new_blob_stoned_copy(js, buf, bytes_read); free(buf); return ret; ) JSC_SCALL(fd_slurp, struct stat st; if (stat(str, &st) != 0) return JS_ThrowInternalError(js, "stat failed: %s", strerror(errno)); if (!S_ISREG(st.st_mode)) return JS_ThrowTypeError(js, "path is not a regular file"); size_t size = st.st_size; if (size == 0) return js_new_blob_stoned_copy(js, NULL, 0); #ifndef _WIN32 int fd = open(str, O_RDONLY); if (fd < 0) return JS_ThrowInternalError(js, "open failed: %s", strerror(errno)); void *data = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); if (data == MAP_FAILED) { close(fd); return JS_ThrowInternalError(js, "mmap failed: %s", strerror(errno)); } ret = js_new_blob_stoned_copy(js, data, size); munmap(data, size); close(fd); #else // Windows: use memory mapping for optimal performance HANDLE hFile = CreateFileA(str, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) return JS_ThrowInternalError(js, "CreateFile failed: %lu", GetLastError()); HANDLE hMapping = CreateFileMappingA(hFile, NULL, PAGE_READONLY, 0, 0, NULL); if (hMapping == NULL) { CloseHandle(hFile); return JS_ThrowInternalError(js, "CreateFileMapping failed: %lu", GetLastError()); } void *data = MapViewOfFile(hMapping, FILE_MAP_READ, 0, 0, 0); if (data == NULL) { CloseHandle(hMapping); CloseHandle(hFile); return JS_ThrowInternalError(js, "MapViewOfFile failed: %lu", GetLastError()); } ret = js_new_blob_stoned_copy(js, data, size); UnmapViewOfFile(data); CloseHandle(hMapping); CloseHandle(hFile); #endif ) JSC_CCALL(fd_lseek, int fd = js2fd(js, argv[0]); if (fd < 0) return JS_EXCEPTION; off_t offset = js2number(js, argv[1]); int whence = SEEK_SET; if (argc > 2) { const char *whence_str = JS_ToCString(js, argv[2]); if (strcmp(whence_str, "cur") == 0) whence = SEEK_CUR; else if (strcmp(whence_str, "end") == 0) whence = SEEK_END; JS_FreeCString(js, whence_str); } off_t new_pos = lseek(fd, offset, whence); if (new_pos < 0) return JS_ThrowInternalError(js, "lseek failed: %s", strerror(errno)); return JS_NewInt64(js, new_pos); ) JSC_CCALL(fd_getcwd, char buf[PATH_MAX]; if (getcwd(buf, sizeof(buf)) == NULL) return JS_ThrowInternalError(js, "getcwd failed: %s", strerror(errno)); return JS_NewString(js, buf); ) JSC_SCALL(fd_rmdir, int recursive = 0; if (argc > 1) recursive = JS_ToBool(js, argv[1]); if (recursive) { // Recursively remove directory contents first #ifdef _WIN32 WIN32_FIND_DATAA ffd; char search_path[PATH_MAX]; snprintf(search_path, sizeof(search_path), "%s\\*", str); HANDLE hFind = FindFirstFileA(search_path, &ffd); if (hFind != INVALID_HANDLE_VALUE) { do { if (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0) continue; char full_path[PATH_MAX]; snprintf(full_path, sizeof(full_path), "%s\\%s", str, ffd.cFileName); if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { JSValue args[2] = { JS_NewString(js, full_path), JS_TRUE }; JSValue result = js_fd_rmdir(js, JS_NULL, 2, args); JS_FreeValue(js, args[0]); if (JS_IsException(result)) { FindClose(hFind); return result; } JS_FreeValue(js, result); } else { if (unlink(full_path) != 0) { FindClose(hFind); ret = JS_ThrowInternalError(js, "could not remove file %s: %s", full_path, strerror(errno)); return ret; } } } while (FindNextFileA(hFind, &ffd)); FindClose(hFind); } #else DIR *dir = opendir(str); if (dir) { struct dirent *entry; while ((entry = readdir(dir)) != NULL) { if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) continue; char full_path[PATH_MAX]; snprintf(full_path, sizeof(full_path), "%s/%s", str, entry->d_name); struct stat st; if (lstat(full_path, &st) == 0 && S_ISDIR(st.st_mode)) { JSValue args[2] = { JS_NewString(js, full_path), JS_TRUE }; JSValue result = js_fd_rmdir(js, JS_NULL, 2, args); JS_FreeValue(js, args[0]); if (JS_IsException(result)) { closedir(dir); return result; } JS_FreeValue(js, result); } else { if (unlink(full_path) != 0) { closedir(dir); ret = JS_ThrowInternalError(js, "could not remove file %s: %s", full_path, strerror(errno)); return ret; } } } closedir(dir); } #endif } if (rmdir(str) != 0) ret = JS_ThrowInternalError(js, "could not remove directory %s: %s", str, strerror(errno)); ) JSC_SCALL(fd_mkdir, if (mkdir(str, 0755) != 0) ret = JS_ThrowInternalError(js, "could not make directory %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"); else if (!JS_IsString(argv[1])) ret = JS_ThrowTypeError(js, "second argument must be a string (new path)"); else { const char *new_path = JS_ToCString(js, argv[1]); if (rename(str, new_path) != 0) ret = JS_ThrowInternalError(js, "could not rename %s to %s: %s", str, new_path, strerror(errno)); JS_FreeCString(js, new_path); } ) JSC_SCALL(fd_symlink, if (argc < 2) ret = JS_ThrowTypeError(js, "fd.symlink requires 2 arguments: target and link path"); else if (!JS_IsString(argv[1])) ret = JS_ThrowTypeError(js, "second argument must be a string (link path)"); else { const char *link_path = JS_ToCString(js, argv[1]); #ifdef _WIN32 if (!CreateSymbolicLinkA(link_path, str, SYMBOLIC_LINK_FLAG_DIRECTORY)) { // Try file if (!CreateSymbolicLinkA(link_path, str, 0)) { ret = JS_ThrowInternalError(js, "could not create symlink %s -> %s: %lu", link_path, str, GetLastError()); } } #else if (symlink(str, link_path) != 0) ret = JS_ThrowInternalError(js, "could not create symlink %s -> %s: %s", link_path, str, strerror(errno)); #endif JS_FreeCString(js, link_path); } ) 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; #ifdef _WIN32 if (stat(path, &st) != 0) #else if (lstat(path, &st) != 0) #endif return -1; if (S_ISDIR(st.st_mode)) { // Directory: remove contents first #ifdef _WIN32 WIN32_FIND_DATA ffd; char search_path[PATH_MAX]; snprintf(search_path, sizeof(search_path), "%s\\*", path); HANDLE hFind = FindFirstFile(search_path, &ffd); if (hFind != INVALID_HANDLE_VALUE) { do { if (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0) continue; char child_path[PATH_MAX]; snprintf(child_path, sizeof(child_path), "%s\\%s", path, ffd.cFileName); if (remove_recursive(child_path) != 0) { FindClose(hFind); return -1; } } while (FindNextFile(hFind, &ffd) != 0); FindClose(hFind); } #else DIR *d = opendir(path); if (d) { struct dirent *dir; while ((dir = readdir(d)) != NULL) { if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) continue; char child_path[PATH_MAX]; snprintf(child_path, sizeof(child_path), "%s/%s", path, dir->d_name); if (remove_recursive(child_path) != 0) { closedir(d); return -1; } } closedir(d); } #endif // Remove the now-empty directory return rmdir(path); } else { // File: just unlink return unlink(path); } } JSC_SCALL(fd_rm, if (remove_recursive(str) != 0) ret = JS_ThrowInternalError(js, "could not remove %s: %s", str, strerror(errno)); ) JSC_CCALL(fd_fsync, int fd = js2fd(js, argv[0]); if (fd < 0) return JS_EXCEPTION; if (fsync(fd) != 0) return JS_ThrowInternalError(js, "fsync failed: %s", strerror(errno)); return JS_NULL; ) JSC_CCALL(fd_close, int fd = js2fd(js, argv[0]); if (fd < 0) return JS_EXCEPTION; if (close(fd) != 0) return JS_ThrowInternalError(js, "close failed: %s", strerror(errno)); return JS_NULL; ) JSC_CCALL(fd_fstat, int fd = js2fd(js, argv[0]); if (fd < 0) return JS_EXCEPTION; struct stat st; if (fstat(fd, &st) != 0) return JS_ThrowInternalError(js, "fstat failed: %s", strerror(errno)); JSValue obj = JS_NewObject(js); JS_SetPropertyStr(js, obj, "size", JS_NewInt64(js, st.st_size)); JS_SetPropertyStr(js, obj, "mode", JS_NewInt32(js, st.st_mode)); JS_SetPropertyStr(js, obj, "uid", JS_NewInt32(js, st.st_uid)); JS_SetPropertyStr(js, obj, "gid", JS_NewInt32(js, st.st_gid)); JS_SetPropertyStr(js, obj, "atime", JS_NewInt64(js, st.st_atime)); JS_SetPropertyStr(js, obj, "mtime", JS_NewInt64(js, st.st_mtime)); JS_SetPropertyStr(js, obj, "ctime", JS_NewInt64(js, st.st_ctime)); JS_SetPropertyStr(js, obj, "nlink", JS_NewInt32(js, st.st_nlink)); JS_SetPropertyStr(js, obj, "ino", JS_NewInt64(js, st.st_ino)); JS_SetPropertyStr(js, obj, "dev", JS_NewInt32(js, st.st_dev)); JS_SetPropertyStr(js, obj, "rdev", JS_NewInt32(js, st.st_rdev)); #ifndef _WIN32 JS_SetPropertyStr(js, obj, "blksize", JS_NewInt32(js, st.st_blksize)); JS_SetPropertyStr(js, obj, "blocks", JS_NewInt64(js, st.st_blocks)); #else JS_SetPropertyStr(js, obj, "blksize", JS_NewInt32(js, 4096)); JS_SetPropertyStr(js, obj, "blocks", JS_NewInt64(js, st.st_size / 512)); #endif // Add boolean properties for file type JS_SetPropertyStr(js, obj, "isFile", JS_NewBool(js, S_ISREG(st.st_mode))); JS_SetPropertyStr(js, obj, "isDirectory", JS_NewBool(js, S_ISDIR(st.st_mode))); JS_SetPropertyStr(js, obj, "isSymlink", JS_NewBool(js, S_ISLNK(st.st_mode))); JS_SetPropertyStr(js, obj, "isFIFO", JS_NewBool(js, S_ISFIFO(st.st_mode))); JS_SetPropertyStr(js, obj, "isSocket", JS_NewBool(js, S_ISSOCK(st.st_mode))); JS_SetPropertyStr(js, obj, "isCharDevice", JS_NewBool(js, S_ISCHR(st.st_mode))); JS_SetPropertyStr(js, obj, "isBlockDevice", JS_NewBool(js, S_ISBLK(st.st_mode))); return obj; ) JSC_CCALL(fd_stat, const char *path = JS_ToCString(js, argv[0]); if (!path) return JS_EXCEPTION; struct stat st; if (stat(path, &st) != 0) { JS_FreeCString(js, path); return JS_NewObject(js); } JSValue obj = JS_NewObject(js); JS_SetPropertyStr(js, obj, "size", JS_NewInt64(js, st.st_size)); JS_SetPropertyStr(js, obj, "mode", JS_NewInt32(js, st.st_mode)); JS_SetPropertyStr(js, obj, "uid", JS_NewInt32(js, st.st_uid)); JS_SetPropertyStr(js, obj, "gid", JS_NewInt32(js, st.st_gid)); JS_SetPropertyStr(js, obj, "atime", JS_NewInt64(js, st.st_atime)); JS_SetPropertyStr(js, obj, "mtime", JS_NewInt64(js, st.st_mtime)); JS_SetPropertyStr(js, obj, "ctime", JS_NewInt64(js, st.st_ctime)); JS_SetPropertyStr(js, obj, "nlink", JS_NewInt32(js, st.st_nlink)); JS_SetPropertyStr(js, obj, "ino", JS_NewInt64(js, st.st_ino)); JS_SetPropertyStr(js, obj, "dev", JS_NewInt32(js, st.st_dev)); JS_SetPropertyStr(js, obj, "rdev", JS_NewInt32(js, st.st_rdev)); #ifndef _WIN32 JS_SetPropertyStr(js, obj, "blksize", JS_NewInt32(js, st.st_blksize)); JS_SetPropertyStr(js, obj, "blocks", JS_NewInt64(js, st.st_blocks)); #else JS_SetPropertyStr(js, obj, "blksize", JS_NewInt32(js, 4096)); JS_SetPropertyStr(js, obj, "blocks", JS_NewInt64(js, st.st_size / 512)); #endif // Add boolean properties for file type JS_SetPropertyStr(js, obj, "isFile", JS_NewBool(js, S_ISREG(st.st_mode))); JS_SetPropertyStr(js, obj, "isDirectory", JS_NewBool(js, S_ISDIR(st.st_mode))); JS_SetPropertyStr(js, obj, "isSymlink", JS_NewBool(js, S_ISLNK(st.st_mode))); JS_SetPropertyStr(js, obj, "isFIFO", JS_NewBool(js, S_ISFIFO(st.st_mode))); JS_SetPropertyStr(js, obj, "isSocket", JS_NewBool(js, S_ISSOCK(st.st_mode))); JS_SetPropertyStr(js, obj, "isCharDevice", JS_NewBool(js, S_ISCHR(st.st_mode))); JS_SetPropertyStr(js, obj, "isBlockDevice", JS_NewBool(js, S_ISBLK(st.st_mode))); JS_FreeCString(js, path); return obj; ) JSC_SCALL(fd_readdir, #ifdef _WIN32 WIN32_FIND_DATA ffd; char path[PATH_MAX]; snprintf(path, sizeof(path), "%s\\*", str); HANDLE hFind = FindFirstFile(path, &ffd); if (hFind == INVALID_HANDLE_VALUE) { ret = JS_ThrowInternalError(js, "FindFirstFile failed for %s", path); } else { ret = JS_NewArray(js); do { if (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0) continue; JS_ArrayPush(js, ret,JS_NewString(js, ffd.cFileName)); } while (FindNextFile(hFind, &ffd) != 0); FindClose(hFind); } #else DIR *d; struct dirent *dir; d = opendir(str); if (d) { ret = JS_NewArray(js); while ((dir = readdir(d)) != NULL) { if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) continue; JS_ArrayPush(js, ret, JS_NewString(js, dir->d_name)); } closedir(d); } else { ret = JS_ThrowInternalError(js, "opendir failed for %s: %s", str, strerror(errno)); } #endif ) JSC_CCALL(fd_is_file, const char *path = JS_ToCString(js, argv[0]); if (!path) return JS_EXCEPTION; struct stat st; if (stat(path, &st) != 0) { JS_FreeCString(js, path); return JS_NewBool(js, 0); } JS_FreeCString(js, path); return JS_NewBool(js, S_ISREG(st.st_mode)); ) JSC_CCALL(fd_is_dir, const char *path = JS_ToCString(js, argv[0]); if (!path) return JS_EXCEPTION; struct stat st; if (stat(path, &st) != 0) { JS_FreeCString(js, path); return JS_NewBool(js, 0); } JS_FreeCString(js, path); return JS_NewBool(js, S_ISDIR(st.st_mode)); ) JSC_CCALL(fd_slurpwrite, size_t len; const char *data = js_get_blob_data(js, &len, argv[1]); if (!data && len > 0) return JS_EXCEPTION; const char *str = JS_ToCString(js, argv[0]); if (!str) return JS_EXCEPTION; int fd = open(str, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd < 0) { JS_FreeCString(js, str); return JS_ThrowInternalError(js, "open failed for %s: %s", str, strerror(errno)); } ssize_t written = write(fd, data, len); close(fd); JS_FreeCString(js, str); if (written != (ssize_t)len) return JS_ThrowInternalError(js, "write failed for %s: %s", str, strerror(errno)); return JS_NULL; ) // Helper function for recursive enumeration static void visit_directory(JSContext *js, JSValue results, int *result_count, const char *curr_path, const char *rel_prefix, int recurse) { if (!curr_path) return; #ifdef _WIN32 WIN32_FIND_DATA ffd; char search_path[PATH_MAX]; snprintf(search_path, sizeof(search_path), "%s\\*", curr_path); HANDLE hFind = FindFirstFile(search_path, &ffd); if (hFind != INVALID_HANDLE_VALUE) { do { if (strcmp(ffd.cFileName, ".") == 0 || strcmp(ffd.cFileName, "..") == 0) continue; char item_rel[PATH_MAX]; if (rel_prefix && strlen(rel_prefix) > 0) { snprintf(item_rel, sizeof(item_rel), "%s/%s", rel_prefix, ffd.cFileName); } else { strcpy(item_rel, ffd.cFileName); } JS_SetPropertyUint32(js, results, (*result_count)++, JS_NewString(js, item_rel)); if (recurse) { struct stat st; char child_path[PATH_MAX]; snprintf(child_path, sizeof(child_path), "%s\\%s", curr_path, ffd.cFileName); if (stat(child_path, &st) == 0 && S_ISDIR(st.st_mode)) { visit_directory(js, results, result_count, child_path, item_rel, recurse); } } } while (FindNextFile(hFind, &ffd) != 0); FindClose(hFind); } #else DIR *d = opendir(curr_path); if (d) { struct dirent *dir; while ((dir = readdir(d)) != NULL) { if (strcmp(dir->d_name, ".") == 0 || strcmp(dir->d_name, "..") == 0) continue; char item_rel[PATH_MAX]; if (rel_prefix && strlen(rel_prefix) > 0) { snprintf(item_rel, sizeof(item_rel), "%s/%s", rel_prefix, dir->d_name); } else { strcpy(item_rel, dir->d_name); } JS_SetPropertyUint32(js, results, (*result_count)++, JS_NewString(js, item_rel)); if (recurse) { struct stat st; char child_path[PATH_MAX]; snprintf(child_path, sizeof(child_path), "%s/%s", curr_path, dir->d_name); if (stat(child_path, &st) == 0 && S_ISDIR(st.st_mode)) { visit_directory(js, results, result_count, child_path, item_rel, recurse); } } } closedir(d); } #endif } JSC_SCALL(fd_enumerate, const char *path = str; if (!path) path = "."; int recurse = 0; if (argc > 1) recurse = JS_ToBool(js, argv[1]); JSValue results = JS_NewArray(js); int result_count = 0; struct stat st; if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) visit_directory(js, results, &result_count, path, "", recurse); 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 for %s: %s", path, 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 for %s: %s", path, 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; #ifdef _WIN32 DWORD attrs = GetFileAttributesA(path); JS_FreeCString(js, path); int is_link = (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_REPARSE_POINT)); return JS_NewBool(js, is_link); #else struct stat st; int is_link = (lstat(path, &st) == 0 && S_ISLNK(st.st_mode)); JS_FreeCString(js, path); return JS_NewBool(js, is_link); #endif ) JSC_CCALL(fd_readlink, const char *path = JS_ToCString(js, argv[0]); if (!path) return JS_EXCEPTION; #ifdef _WIN32 JS_FreeCString(js, path); return JS_ThrowInternalError(js, "readlink not supported on Windows"); #else char buf[PATH_MAX]; ssize_t len = readlink(path, buf, sizeof(buf) - 1); JS_FreeCString(js, path); if (len < 0) { return JS_ThrowInternalError(js, "readlink failed: %s", strerror(errno)); } buf[len] = '\0'; return JS_NewString(js, buf); #endif ) static const JSCFunctionListEntry js_fd_funcs[] = { MIST_FUNC_DEF(fd, open, 2), MIST_FUNC_DEF(fd, write, 2), MIST_FUNC_DEF(fd, read, 2), MIST_FUNC_DEF(fd, slurp, 1), MIST_FUNC_DEF(fd, slurpwrite, 2), MIST_FUNC_DEF(fd, lseek, 3), MIST_FUNC_DEF(fd, getcwd, 0), MIST_FUNC_DEF(fd, rmdir, 2), MIST_FUNC_DEF(fd, unlink, 1), MIST_FUNC_DEF(fd, mkdir, 1), MIST_FUNC_DEF(fd, mv, 2), MIST_FUNC_DEF(fd, rm, 1), MIST_FUNC_DEF(fd, fsync, 1), MIST_FUNC_DEF(fd, close, 1), MIST_FUNC_DEF(fd, stat, 1), MIST_FUNC_DEF(fd, fstat, 1), 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), MIST_FUNC_DEF(fd, readlink, 1), }; JSValue js_fd_use(JSContext *js) { JSValue mod = JS_NewObject(js); JS_SetPropertyFunctionList(js, mod, js_fd_funcs, countof(js_fd_funcs)); return mod; }