Files
cell/source/qjs_fd.c
John Alanbrook c87f85cf6c
Some checks failed
Build and Deploy / build-macos (push) Failing after 29s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
Update files for cross compilation fixes; add dockerfiles for windows/linux/emscripten builds; add commands to makefile to build via dockerfiles
2025-07-12 20:39:25 -05:00

234 lines
6.8 KiB
C

#include "qjs_fd.h"
#include "qjs_blob.h"
#include "jsffi.h"
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#ifdef _WIN32
#include <io.h>
#include <direct.h>
#define mkdir(path, mode) _mkdir(path)
#define rmdir _rmdir
#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 <unistd.h>
#include <dirent.h>
#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)
{
size_t len;
ssize_t wrote;
if (JS_IsString(val)) {
const char *data = JS_ToCStringLen(js, &len, val);
wrote = write(fd, data, len);
JS_FreeCString(js, data);
} else {
unsigned char *data = js_get_blob_data(js, &len, val);
wrote = write(fd, data, len);
}
return wrote;
}
// 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_ThrowReferenceError(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;
ssize_t wrote = js_fd_write_helper(js, fd, argv[1]);
if (wrote < 0)
return JS_ThrowReferenceError(js, "write failed: %s", strerror(errno));
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_ThrowReferenceError(js, "malloc failed");
ssize_t bytes_read = read(fd, buf, size);
if (bytes_read < 0) {
free(buf);
return JS_ThrowReferenceError(js, "read failed: %s", strerror(errno));
}
ret = js_new_blob_stoned_copy(js, buf, bytes_read);
free(buf);
return ret;
)
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_ThrowReferenceError(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_ThrowReferenceError(js, "getcwd failed: %s", strerror(errno));
return JS_NewString(js, buf);
)
JSC_SCALL(fd_rmdir,
if (rmdir(str) != 0)
ret = JS_ThrowReferenceError(js, "could not remove directory %s: %s", str, strerror(errno));
)
JSC_SCALL(fd_mkdir,
if (mkdir(str, 0755) != 0)
ret = JS_ThrowReferenceError(js, "could not make directory %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_ThrowReferenceError(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_ThrowReferenceError(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_ThrowReferenceError(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;
)
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, lseek, 3),
MIST_FUNC_DEF(fd, getcwd, 0),
MIST_FUNC_DEF(fd, rmdir, 1),
MIST_FUNC_DEF(fd, mkdir, 1),
MIST_FUNC_DEF(fd, fsync, 1),
MIST_FUNC_DEF(fd, close, 1),
MIST_FUNC_DEF(fd, fstat, 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;
}