add fd module
This commit is contained in:
@@ -195,7 +195,7 @@ sources = []
|
||||
src += [
|
||||
'anim.c', 'config.c', 'datastream.c','font.c','HandmadeMath.c','jsffi.c','model.c',
|
||||
'render.c','simplex.c','spline.c', 'transform.c','prosperon.c', 'wildmatch.c',
|
||||
'sprite.c', 'rtree.c', 'qjs_nota.c', 'qjs_soloud.c', 'qjs_sdl.c', 'qjs_sdl_video.c', 'qjs_sdl_surface.c', 'qjs_math.c', 'qjs_geometry.c', 'qjs_transform.c', 'qjs_sprite.c', 'qjs_io.c', 'qjs_os.c', 'qjs_actor.c',
|
||||
'sprite.c', 'rtree.c', 'qjs_nota.c', 'qjs_soloud.c', 'qjs_sdl.c', 'qjs_sdl_video.c', 'qjs_sdl_surface.c', 'qjs_math.c', 'qjs_geometry.c', 'qjs_transform.c', 'qjs_sprite.c', 'qjs_io.c', 'qjs_fd.c', 'qjs_os.c', 'qjs_actor.c',
|
||||
'qjs_qr.c', 'qjs_wota.c', 'monocypher.c', 'qjs_blob.c', 'qjs_crypto.c', 'qjs_time.c', 'qjs_http.c', 'qjs_rtree.c', 'qjs_spline.c', 'qjs_js.c', 'qjs_debug.c'
|
||||
]
|
||||
# quirc src
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "datastream.h"
|
||||
#include "qjs_sdl.h"
|
||||
#include "qjs_io.h"
|
||||
#include "qjs_fd.h"
|
||||
#include "transform.h"
|
||||
#include "stb_ds.h"
|
||||
#include "stb_image.h"
|
||||
@@ -1586,6 +1587,7 @@ void ffi_load(JSContext *js)
|
||||
|
||||
// extra
|
||||
arrput(rt->module_registry, ((ModuleEntry){"io", js_io_use}));
|
||||
arrput(rt->module_registry, ((ModuleEntry){"fd", js_fd_use}));
|
||||
arrput(rt->module_registry, ((ModuleEntry){"os", js_os_use}));
|
||||
arrput(rt->module_registry, MISTLINE(qr));
|
||||
arrput(rt->module_registry, MISTLINE(http));
|
||||
|
||||
497
source/qjs_fd.c
Normal file
497
source/qjs_fd.c
Normal file
@@ -0,0 +1,497 @@
|
||||
#include "qjs_fd.h"
|
||||
#include "jsffi.h"
|
||||
#include "qjs_macros.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include "wildmatch.h"
|
||||
|
||||
// File descriptor wrapper structure
|
||||
typedef struct {
|
||||
int fd;
|
||||
} FDWrapper;
|
||||
|
||||
// Free function for file descriptor
|
||||
static void FD_free(JSRuntime *rt, FDWrapper *fdw)
|
||||
{
|
||||
if (fdw->fd >= 0) {
|
||||
close(fdw->fd);
|
||||
}
|
||||
js_free_rt(rt, fdw);
|
||||
}
|
||||
|
||||
// Class definition for file descriptor
|
||||
static JSClassID js_fd_class_id;
|
||||
|
||||
static JSClassDef js_fd_class = {
|
||||
"FileDescriptor",
|
||||
.finalizer = (JSClassFinalizer *)FD_free,
|
||||
};
|
||||
|
||||
// Helper to convert JS value to FDWrapper
|
||||
static FDWrapper *js2fd(JSContext *ctx, JSValueConst obj)
|
||||
{
|
||||
return JS_GetOpaque2(ctx, obj, js_fd_class_id);
|
||||
}
|
||||
|
||||
// Helper to create JS FDWrapper object
|
||||
static JSValue fd2js(JSContext *ctx, int fd)
|
||||
{
|
||||
FDWrapper *fdw = js_mallocz(ctx, sizeof(FDWrapper));
|
||||
if (!fdw) return JS_EXCEPTION;
|
||||
|
||||
fdw->fd = fd;
|
||||
|
||||
JSValue obj = JS_NewObjectClass(ctx, js_fd_class_id);
|
||||
if (JS_IsException(obj)) {
|
||||
js_free(ctx, fdw);
|
||||
return obj;
|
||||
}
|
||||
|
||||
JS_SetOpaque(obj, fdw);
|
||||
return obj;
|
||||
}
|
||||
|
||||
// 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_GetArrayBuffer(js, &len, val);
|
||||
wrote = write(fd, data, len);
|
||||
}
|
||||
return wrote;
|
||||
}
|
||||
|
||||
// Glob data structure
|
||||
struct fd_globdata {
|
||||
JSContext *js;
|
||||
JSValue arr;
|
||||
char **globs;
|
||||
int idx;
|
||||
int recurse;
|
||||
};
|
||||
|
||||
// Helper for recursive directory enumeration
|
||||
static void enumerate_dir(struct fd_globdata *data, const char *path);
|
||||
|
||||
// Callback for glob matching
|
||||
static void globfs_cb(struct fd_globdata *data, const char *dir, const char *file)
|
||||
{
|
||||
char *path;
|
||||
int needfree = 0;
|
||||
|
||||
if (dir[0] == 0) {
|
||||
path = (char*)file;
|
||||
} else {
|
||||
size_t len = strlen(dir) + strlen(file) + 2;
|
||||
path = malloc(len);
|
||||
snprintf(path, len, "%s/%s", dir, file);
|
||||
needfree = 1;
|
||||
}
|
||||
|
||||
char **glob = data->globs;
|
||||
while (*glob != NULL) {
|
||||
if (wildmatch(*glob, path, WM_WILDSTAR) == WM_MATCH)
|
||||
goto END;
|
||||
glob++;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) {
|
||||
enumerate_dir(data, path);
|
||||
goto END;
|
||||
}
|
||||
|
||||
JS_SetPropertyUint32(data->js, data->arr, data->idx++, JS_NewString(data->js, path));
|
||||
|
||||
END:
|
||||
if (needfree) free(path);
|
||||
}
|
||||
|
||||
// Helper for directory enumeration
|
||||
static void enumerate_dir(struct fd_globdata *data, const char *path)
|
||||
{
|
||||
DIR *dir = opendir(path);
|
||||
if (!dir) return;
|
||||
|
||||
struct dirent *entry;
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
|
||||
globfs_cb(data, path, entry->d_name);
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
}
|
||||
|
||||
// FD I/O FUNCTIONS
|
||||
|
||||
JSC_SCALL(fd_rm,
|
||||
if (remove(str) != 0)
|
||||
ret = JS_ThrowReferenceError(js, "could not remove %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_SCALL(fd_exists,
|
||||
struct stat st;
|
||||
ret = JS_NewBool(js, stat(str, &st) == 0);
|
||||
)
|
||||
|
||||
JSC_SCALL(fd_stat,
|
||||
struct stat st;
|
||||
if (stat(str, &st) != 0)
|
||||
return JS_ThrowReferenceError(js, "stat failed: %s", strerror(errno));
|
||||
|
||||
ret = JS_NewObject(js);
|
||||
JS_SetPropertyStr(js, ret, "filesize", number2js(js, st.st_size));
|
||||
JS_SetPropertyStr(js, ret, "modtime", number2js(js, st.st_mtime));
|
||||
JS_SetPropertyStr(js, ret, "createtime", number2js(js, st.st_ctime));
|
||||
JS_SetPropertyStr(js, ret, "accesstime", number2js(js, st.st_atime));
|
||||
JS_SetPropertyStr(js, ret, "mode", JS_NewInt32(js, st.st_mode));
|
||||
JS_SetPropertyStr(js, ret, "is_directory", JS_NewBool(js, S_ISDIR(st.st_mode)));
|
||||
JS_SetPropertyStr(js, ret, "is_file", JS_NewBool(js, S_ISREG(st.st_mode)));
|
||||
)
|
||||
|
||||
JSC_SCALL(fd_slurpbytes,
|
||||
int fd = open(str, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
ret = JS_ThrowReferenceError(js, "could not open %s: %s", str, strerror(errno));
|
||||
goto END;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) != 0) {
|
||||
close(fd);
|
||||
ret = JS_ThrowReferenceError(js, "fstat failed: %s", strerror(errno));
|
||||
goto END;
|
||||
}
|
||||
|
||||
void *data = malloc(st.st_size);
|
||||
if (!data) {
|
||||
close(fd);
|
||||
ret = JS_ThrowReferenceError(js, "malloc failed");
|
||||
goto END;
|
||||
}
|
||||
|
||||
ssize_t bytes_read = read(fd, data, st.st_size);
|
||||
close(fd);
|
||||
|
||||
if (bytes_read != st.st_size) {
|
||||
free(data);
|
||||
ret = JS_ThrowReferenceError(js, "read failed: %s", strerror(errno));
|
||||
goto END;
|
||||
}
|
||||
|
||||
ret = JS_NewArrayBufferCopy(js, data, st.st_size);
|
||||
free(data);
|
||||
|
||||
END:
|
||||
)
|
||||
|
||||
JSC_SCALL(fd_slurp,
|
||||
int fd = open(str, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
ret = JS_ThrowReferenceError(js, "could not open %s: %s", str, strerror(errno));
|
||||
goto END;
|
||||
}
|
||||
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) != 0) {
|
||||
close(fd);
|
||||
ret = JS_ThrowReferenceError(js, "fstat failed: %s", strerror(errno));
|
||||
goto END;
|
||||
}
|
||||
|
||||
char *data = malloc(st.st_size + 1);
|
||||
if (!data) {
|
||||
close(fd);
|
||||
ret = JS_ThrowReferenceError(js, "malloc failed");
|
||||
goto END;
|
||||
}
|
||||
|
||||
ssize_t bytes_read = read(fd, data, st.st_size);
|
||||
close(fd);
|
||||
|
||||
if (bytes_read != st.st_size) {
|
||||
free(data);
|
||||
ret = JS_ThrowReferenceError(js, "read failed: %s", strerror(errno));
|
||||
goto END;
|
||||
}
|
||||
|
||||
data[st.st_size] = '\0';
|
||||
ret = JS_NewStringLen(js, data, st.st_size);
|
||||
free(data);
|
||||
|
||||
END:
|
||||
)
|
||||
|
||||
JSC_SCALL(fd_slurpwrite,
|
||||
int fd = open(str, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
if (fd < 0) {
|
||||
ret = JS_ThrowReferenceError(js, "could not open %s: %s", str, strerror(errno));
|
||||
goto END;
|
||||
}
|
||||
|
||||
ssize_t wrote = js_fd_write_helper(js, fd, argv[1]);
|
||||
close(fd);
|
||||
|
||||
if (wrote < 0)
|
||||
ret = JS_ThrowReferenceError(js, "write failed: %s", strerror(errno));
|
||||
|
||||
END:
|
||||
)
|
||||
|
||||
JSC_SSCALL(fd_match,
|
||||
if (wildmatch(str, str2, WM_PATHNAME | WM_PERIOD | WM_WILDSTAR) == WM_MATCH)
|
||||
ret = JS_NewBool(js, 1);
|
||||
else
|
||||
ret = JS_NewBool(js, 0);
|
||||
)
|
||||
|
||||
JSC_CCALL(fd_globfs,
|
||||
ret = JS_NewArray(js);
|
||||
struct fd_globdata data;
|
||||
data.js = js;
|
||||
data.arr = ret;
|
||||
data.idx = 0;
|
||||
data.recurse = 1;
|
||||
|
||||
int globs_len = JS_ArrayLength(js, argv[0]);
|
||||
const char *globs[globs_len + 1];
|
||||
for (int i = 0; i < globs_len; i++) {
|
||||
JSValue g = JS_GetPropertyUint32(js, argv[0], i);
|
||||
globs[i] = JS_ToCString(js, g);
|
||||
JS_FreeValue(js, g);
|
||||
}
|
||||
globs[globs_len] = NULL;
|
||||
data.globs = (char**)globs;
|
||||
|
||||
const char *path = ".";
|
||||
if (!JS_IsUndefined(argv[1]))
|
||||
path = JS_ToCString(js, argv[1]);
|
||||
|
||||
enumerate_dir(&data, path);
|
||||
|
||||
for (int i = 0; i < globs_len; i++)
|
||||
JS_FreeCString(js, globs[i]);
|
||||
|
||||
if (!JS_IsUndefined(argv[1]))
|
||||
JS_FreeCString(js, path);
|
||||
)
|
||||
|
||||
JSC_SCALL(fd_enumerate,
|
||||
ret = JS_NewArray(js);
|
||||
|
||||
DIR *dir = opendir(str);
|
||||
if (!dir) {
|
||||
ret = JS_ThrowReferenceError(js, "could not open directory %s: %s", str, strerror(errno));
|
||||
goto END;
|
||||
}
|
||||
|
||||
int idx = 0;
|
||||
struct dirent *entry;
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
|
||||
continue;
|
||||
|
||||
JS_SetPropertyUint32(js, ret, idx++, JS_NewString(js, entry->d_name));
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
|
||||
END:
|
||||
)
|
||||
|
||||
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_chdir,
|
||||
if (chdir(str) != 0)
|
||||
ret = JS_ThrowReferenceError(js, "chdir failed: %s", strerror(errno));
|
||||
)
|
||||
|
||||
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 = fd2js(js, fd);
|
||||
)
|
||||
|
||||
JSC_SCALL(fd_is_directory,
|
||||
struct stat st;
|
||||
if (stat(str, &st) != 0)
|
||||
ret = JS_NewBool(js, 0);
|
||||
else
|
||||
ret = JS_NewBool(js, S_ISDIR(st.st_mode));
|
||||
)
|
||||
|
||||
// FILE DESCRIPTOR FUNCTIONS
|
||||
|
||||
JSC_CCALL(file_close,
|
||||
FDWrapper *fdw = js2fd(js, self);
|
||||
if (!fdw) return JS_EXCEPTION;
|
||||
|
||||
if (fdw->fd >= 0) {
|
||||
close(fdw->fd);
|
||||
fdw->fd = -1;
|
||||
}
|
||||
)
|
||||
|
||||
JSC_CCALL(file_write,
|
||||
FDWrapper *fdw = js2fd(js, self);
|
||||
if (!fdw) return JS_EXCEPTION;
|
||||
|
||||
ssize_t wrote = js_fd_write_helper(js, fdw->fd, argv[0]);
|
||||
if (wrote < 0)
|
||||
return JS_ThrowReferenceError(js, "write failed: %s", strerror(errno));
|
||||
|
||||
return JS_NewInt64(js, wrote);
|
||||
)
|
||||
|
||||
JSC_CCALL(file_read,
|
||||
FDWrapper *fdw = js2fd(js, self);
|
||||
if (!fdw) return JS_EXCEPTION;
|
||||
|
||||
size_t size = 4096;
|
||||
if (argc > 0)
|
||||
size = js2number(js, argv[0]);
|
||||
|
||||
void *buf = malloc(size);
|
||||
if (!buf)
|
||||
return JS_ThrowReferenceError(js, "malloc failed");
|
||||
|
||||
ssize_t bytes_read = read(fdw->fd, buf, size);
|
||||
if (bytes_read < 0) {
|
||||
free(buf);
|
||||
return JS_ThrowReferenceError(js, "read failed: %s", strerror(errno));
|
||||
}
|
||||
|
||||
ret = JS_NewArrayBufferCopy(js, buf, bytes_read);
|
||||
free(buf);
|
||||
return ret;
|
||||
)
|
||||
|
||||
JSC_CCALL(file_seek,
|
||||
FDWrapper *fdw = js2fd(js, self);
|
||||
if (!fdw) return JS_EXCEPTION;
|
||||
|
||||
off_t offset = js2number(js, argv[0]);
|
||||
int whence = SEEK_SET;
|
||||
|
||||
if (argc > 1) {
|
||||
const char *whence_str = JS_ToCString(js, argv[1]);
|
||||
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(fdw->fd, offset, whence);
|
||||
if (new_pos < 0)
|
||||
return JS_ThrowReferenceError(js, "lseek failed: %s", strerror(errno));
|
||||
|
||||
return JS_NewInt64(js, new_pos);
|
||||
)
|
||||
|
||||
JSC_CCALL(file_tell,
|
||||
FDWrapper *fdw = js2fd(js, self);
|
||||
if (!fdw) return JS_EXCEPTION;
|
||||
|
||||
off_t pos = lseek(fdw->fd, 0, SEEK_CUR);
|
||||
if (pos < 0)
|
||||
return JS_ThrowReferenceError(js, "lseek failed: %s", strerror(errno));
|
||||
|
||||
return JS_NewInt64(js, pos);
|
||||
)
|
||||
|
||||
JSC_CCALL(file_eof,
|
||||
FDWrapper *fdw = js2fd(js, self);
|
||||
if (!fdw) return JS_EXCEPTION;
|
||||
|
||||
off_t cur = lseek(fdw->fd, 0, SEEK_CUR);
|
||||
off_t end = lseek(fdw->fd, 0, SEEK_END);
|
||||
lseek(fdw->fd, cur, SEEK_SET);
|
||||
|
||||
return JS_NewBool(js, cur >= end);
|
||||
)
|
||||
|
||||
static const JSCFunctionListEntry js_fd_funcs[] = {
|
||||
MIST_FUNC_DEF(fd, rm, 1),
|
||||
MIST_FUNC_DEF(fd, mkdir, 1),
|
||||
MIST_FUNC_DEF(fd, stat, 1),
|
||||
MIST_FUNC_DEF(fd, globfs, 2),
|
||||
MIST_FUNC_DEF(fd, match, 2),
|
||||
MIST_FUNC_DEF(fd, exists, 1),
|
||||
MIST_FUNC_DEF(fd, slurp, 1),
|
||||
MIST_FUNC_DEF(fd, slurpbytes, 1),
|
||||
MIST_FUNC_DEF(fd, slurpwrite, 2),
|
||||
MIST_FUNC_DEF(fd, getcwd, 0),
|
||||
MIST_FUNC_DEF(fd, chdir, 1),
|
||||
MIST_FUNC_DEF(fd, open, 2),
|
||||
MIST_FUNC_DEF(fd, enumerate, 1),
|
||||
MIST_FUNC_DEF(fd, is_directory, 1),
|
||||
};
|
||||
|
||||
static const JSCFunctionListEntry js_fd_file_funcs[] = {
|
||||
MIST_FUNC_DEF(file, close, 0),
|
||||
MIST_FUNC_DEF(file, write, 1),
|
||||
MIST_FUNC_DEF(file, read, 1),
|
||||
MIST_FUNC_DEF(file, seek, 2),
|
||||
MIST_FUNC_DEF(file, tell, 0),
|
||||
MIST_FUNC_DEF(file, eof, 0),
|
||||
};
|
||||
|
||||
JSValue js_fd_use(JSContext *js) {
|
||||
// Initialize the file descriptor class
|
||||
JS_NewClassID(&js_fd_class_id);
|
||||
JS_NewClass(JS_GetRuntime(js), js_fd_class_id, &js_fd_class);
|
||||
|
||||
JSValue proto = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, proto, js_fd_file_funcs, countof(js_fd_file_funcs));
|
||||
JS_SetClassProto(js, js_fd_class_id, proto);
|
||||
|
||||
JSValue mod = JS_NewObject(js);
|
||||
JS_SetPropertyFunctionList(js, mod, js_fd_funcs, countof(js_fd_funcs));
|
||||
return mod;
|
||||
}
|
||||
8
source/qjs_fd.h
Normal file
8
source/qjs_fd.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#ifndef QJS_FD_H
|
||||
#define QJS_FD_H
|
||||
|
||||
#include <quickjs.h>
|
||||
|
||||
JSValue js_fd_use(JSContext *js);
|
||||
|
||||
#endif // QJS_FD_H
|
||||
Reference in New Issue
Block a user