229 lines
5.7 KiB
C
229 lines
5.7 KiB
C
#include "qjs_fd.h"
|
|
#include "qjs_blob.h"
|
|
#include "jsffi.h"
|
|
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <dirent.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <stdio.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)
|
|
{
|
|
FDWrapper *fdw = JS_GetOpaque2(ctx, obj, js_fd_class_id);
|
|
if (!fdw) {
|
|
JS_ThrowTypeError(ctx, "Expected file descriptor object");
|
|
return NULL;
|
|
}
|
|
return fdw;
|
|
}
|
|
|
|
// 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_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 = fd2js(js, fd);
|
|
)
|
|
|
|
JSC_CCALL(fd_write,
|
|
FDWrapper *fdw = js2fd(js, argv[0]);
|
|
if (!fdw) return JS_EXCEPTION;
|
|
|
|
ssize_t wrote = js_fd_write_helper(js, fdw->fd, argv[1]);
|
|
if (wrote < 0)
|
|
return JS_ThrowReferenceError(js, "write failed: %s", strerror(errno));
|
|
|
|
return JS_NewInt64(js, wrote);
|
|
)
|
|
|
|
JSC_CCALL(fd_read,
|
|
FDWrapper *fdw = js2fd(js, argv[0]);
|
|
if (!fdw) 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(fdw->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,
|
|
FDWrapper *fdw = js2fd(js, argv[0]);
|
|
if (!fdw) 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(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(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,
|
|
FDWrapper *fdw = js2fd(js, argv[0]);
|
|
if (!fdw) return JS_EXCEPTION;
|
|
|
|
if (fsync(fdw->fd) != 0)
|
|
return JS_ThrowReferenceError(js, "fsync failed: %s", strerror(errno));
|
|
|
|
return JS_UNDEFINED;
|
|
)
|
|
|
|
JSC_CCALL(fd_close,
|
|
FDWrapper *fdw = js2fd(js, argv[0]);
|
|
if (!fdw) return JS_EXCEPTION;
|
|
|
|
if (fdw->fd >= 0) {
|
|
close(fdw->fd);
|
|
fdw->fd = -1;
|
|
}
|
|
|
|
return JS_UNDEFINED;
|
|
)
|
|
|
|
|
|
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),
|
|
};
|
|
|
|
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_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;
|
|
} |