Files
cell/source/qjs_io.c
2025-05-29 11:28:51 -05:00

382 lines
9.8 KiB
C

#include "qjs_io.h"
#include "jsffi.h"
#include "qjs_macros.h"
#include "prosperon.h"
#include <physfs.h>
#include <string.h>
#include <stdlib.h>
#include "wildmatch.h"
// Helper function for array length using QuickJS
// JS_ArrayLength removed - use JS_ArrayLength directly
// PHYSFS_File free function
static void PHYSFS_File_free(JSRuntime *rt, PHYSFS_File *f)
{
PHYSFS_close(f);
}
// Class definition for PHYSFS_File
QJSCLASS(PHYSFS_File,)
// Helper function for writing to PHYSFS
static size_t js_physfs_write(JSContext *js, PHYSFS_File *f, JSValue val)
{
size_t len;
size_t wrote;
if (JS_IsString(val)) {
const char *data = JS_ToCStringLen(js,&len,val);
wrote = PHYSFS_writeBytes(f,data,len);
JS_FreeCString(js,data);
} else {
unsigned char *data = js_get_blob_data(js,&len,val);
wrote = PHYSFS_writeBytes(f,data,len);
}
if (wrote < len) wrote = -1;
return wrote;
}
// Glob data structure for filesystem operations
struct globdata {
JSContext *js;
JSValue arr;
char **globs;
char *glob;
int idx;
int recurse;
};
// Callback for globfs
static int globfs_cb(struct globdata *data, char *dir, char *file)
{
int needfree = 0;
char *path;
if (dir[0] == 0) path = file;
else {
path = malloc(strlen(dir) + strlen(file) + 2);
path[0] = 0;
strcat(path,dir);
strcat(path,"/");
strcat(path,file);
needfree = 1;
}
char **glob = data->globs;
while (*glob != NULL) {
if (wildmatch(*glob, path, WM_WILDSTAR) == WM_MATCH)
goto END;
glob++;
}
PHYSFS_Stat stat;
PHYSFS_stat(path, &stat);
if (stat.filetype == PHYSFS_FILETYPE_DIRECTORY) {
PHYSFS_enumerate(path, globfs_cb, data);
goto END;
}
JS_SetPropertyUint32(data->js, data->arr, data->idx++, JS_NewString(data->js,path));
END:
if (needfree) free(path);
return 1;
}
// Callback for enumerate
static int enumerate_cb(void *udata, const char *dir, const char *fname)
{
struct globdata *data = (struct globdata*)udata;
char *path;
int needfree = 0;
if (dir[0] == 0) path = (char*)fname;
else {
size_t dlen = strlen(dir);
int ends_slash = (dlen && dir[dlen - 1] == '/');
path = malloc(dlen + strlen(fname) + (ends_slash ? 1 : 2));
if (ends_slash) sprintf(path, "%s%s", dir, fname); else sprintf(path, "%s/%s", dir, fname);
needfree = 1;
}
PHYSFS_Stat st;
if (!PHYSFS_stat(path, &st)) {
if (needfree) free(path);
return 1;
}
/* If it's a directory and we're recursing, enumerate it further. */
if (st.filetype == PHYSFS_FILETYPE_DIRECTORY && data->recurse)
PHYSFS_enumerate(path, enumerate_cb, data);
/* Add this item (file or directory) to the JS array. */
JS_SetPropertyUint32(data->js, data->arr, data->idx++, JS_NewString(data->js, path));
if (needfree) free(path);
return 1; /* continue enumerating */
}
// I/O FUNCTIONS
JSC_SCALL(io_rm,
if (!PHYSFS_delete(str)) ret = JS_ThrowReferenceError(js,"could not remove %s. %s", str, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
)
JSC_SCALL(io_mkdir,
if (!PHYSFS_mkdir(str)) ret = JS_ThrowReferenceError(js,"could not make directory %s. %s", str, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
)
JSC_SCALL(io_exists, ret = JS_NewBool(js,PHYSFS_exists(str)); )
JSC_SCALL(io_stat,
PHYSFS_Stat stat;
if (!PHYSFS_stat(str, &stat))
return JS_ThrowReferenceError(js, "%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
ret = JS_NewObject(js);
JS_SetPropertyStr(js,ret,"filesize", number2js(js,stat.filesize));
JS_SetPropertyStr(js,ret,"modtime", number2js(js,stat.modtime));
JS_SetPropertyStr(js,ret,"createtime", number2js(js,stat.createtime));
JS_SetPropertyStr(js,ret,"accesstime", number2js(js,stat.accesstime));
)
JSC_SCALL(io_slurpbytes,
PHYSFS_File *f = PHYSFS_openRead(str);
if (!f) {
ret = JS_ThrowReferenceError(js,"physfs error when slurping %s: %s", str, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
goto END;
}
PHYSFS_Stat stat;
PHYSFS_stat(str,&stat);
void *data = malloc(stat.filesize);
PHYSFS_readBytes(f,data,stat.filesize);
PHYSFS_close(f);
ret = js_new_blob_stoned_copy(js,data,stat.filesize);
END:
)
JSC_SCALL(io_slurp,
PHYSFS_File *f = PHYSFS_openRead(str);
if (!f) {
ret = JS_ThrowReferenceError(js,"physfs error when slurping %s: %s", str, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
goto END;
}
PHYSFS_Stat stat;
PHYSFS_stat(str,&stat);
void *data = malloc(stat.filesize);
PHYSFS_readBytes(f,data,stat.filesize);
PHYSFS_close(f);
ret = JS_NewStringLen(js,data, stat.filesize);
free(data);
END:
)
JSC_SCALL(io_slurpwrite,
PHYSFS_File *f = PHYSFS_openWrite(str);
if (!f) {
ret = JS_ThrowReferenceError(js,"could not write to %s: %s", str, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
goto END;
}
size_t wrote = js_physfs_write(js,f,argv[1]);
PHYSFS_close(f);
if (wrote == -1)
ret = JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
END:
)
JSC_SSCALL(io_mount,
if (!PHYSFS_mount(str,str2,0)) ret = JS_ThrowReferenceError(js,"Unable to mount %s at %s: %s", str, str2, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
)
JSC_SCALL(io_unmount,
if (!PHYSFS_unmount(str)) ret = JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
)
JSC_SCALL(io_writepath,
if (!PHYSFS_setWriteDir(str)) ret = JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
)
JSC_SSCALL(io_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(io_globfs,
ret = JS_NewArray(js);
struct globdata data;
data.js = js;
data.arr = ret;
data.idx = 0;
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 = globs;
const char *path = NULL;
if (!JS_IsUndefined(argv[1])) path = JS_ToCString(js,argv[1]);
PHYSFS_enumerate(path, globfs_cb, &data);
for (int i = 0; i < globs_len; i++)
JS_FreeCString(js,globs[i]);
ret = data.arr;
JS_FreeCString(js,path);
)
JSC_SCALL(io_enumerate,
/* First argument: str (directory name) */
/* Second argument: boolean => recurse or not */
ret = JS_NewArray(js);
struct globdata data;
data.js = js;
data.arr = ret;
data.idx = 0;
data.glob = NULL; /* not used here */
data.globs = NULL; /* not used here */
data.recurse = 0;
if (!JS_IsUndefined(argv[1])) /* parse second arg if provided */
data.recurse = JS_ToBool(js, argv[1]);
/* Enumerate the directory given by 'str'. */
PHYSFS_enumerate(str, enumerate_cb, &data);
/* Return the JS array we filled. */
ret = data.arr;
)
JSC_CCALL(io_basedir, return JS_NewString(js,PHYSFS_getBaseDir()))
JSC_SSCALL(io_prefdir, return JS_NewString(js,PHYSFS_getPrefDir(str, str2)))
JSC_SCALL(io_open,
PHYSFS_File *f = PHYSFS_openWrite(str);
if (!f)
ret = JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
else
ret = PHYSFS_File2js(js,f);
)
JSC_SCALL(io_realdir,
const char *real = PHYSFS_getRealDir(str);
if (!real)
ret = JS_UNDEFINED;
else
ret = JS_NewString(js,real);
)
JSC_CCALL(io_searchpath,
ret = JS_NewArray(js);
char **paths = PHYSFS_getSearchPath();
for (int i = 0; paths[i] != NULL; i++)
JS_SetPropertyUint32(js,ret,i,JS_NewString(js,paths[i]));
)
JSC_CCALL(io_mount_core,
int mount = JS_ToBool(js,argv[0]);
if (!mount)
PHYSFS_unmount("core.zip");
else
prosperon_mount_core();
)
JSC_SCALL(io_is_directory,
PHYSFS_Stat stat;
int good = PHYSFS_stat(str, &stat);
if (!good)
ret = JS_NewBool(js, 0);
else
ret = JS_NewBool(js, stat.filetype == PHYSFS_FILETYPE_DIRECTORY);
)
// FILE FUNCTIONS
JSC_CCALL(file_close,
PHYSFS_File *f = js2PHYSFS_File(js,self);
PHYSFS_close(f);
)
JSC_CCALL(file_write,
PHYSFS_File *f = js2PHYSFS_File(js,self);
size_t wrote = js_physfs_write(js,f,argv[0]);
if (wrote == -1)
return JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
)
JSC_CCALL(file_buffer,
PHYSFS_File *f = js2PHYSFS_File(js,self);
size_t size = js2number(js,argv[0]);
if (!PHYSFS_setBuffer(f,size))
return JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
)
JSC_CCALL(file_tell,
PHYSFS_File *f = js2PHYSFS_File(js,self);
size_t tell = PHYSFS_tell(f);
if (tell == -1)
return JS_ThrowReferenceError(js,"%s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
return number2js(js,tell);
)
JSC_CCALL(file_eof,
PHYSFS_File *f = js2PHYSFS_File(js,self);
return JS_NewBool(js, PHYSFS_eof(f));
)
static const JSCFunctionListEntry js_io_funcs[] = {
MIST_FUNC_DEF(io, rm, 1),
MIST_FUNC_DEF(io, mkdir, 1),
MIST_FUNC_DEF(io,stat,1),
MIST_FUNC_DEF(io, globfs, 2),
MIST_FUNC_DEF(io, match, 2),
MIST_FUNC_DEF(io, exists, 1),
MIST_FUNC_DEF(io, mount, 2),
MIST_FUNC_DEF(io,unmount,1),
MIST_FUNC_DEF(io,slurp,1),
MIST_FUNC_DEF(io,slurpbytes,1),
MIST_FUNC_DEF(io,slurpwrite,2),
MIST_FUNC_DEF(io,writepath, 1),
MIST_FUNC_DEF(io,basedir, 0),
MIST_FUNC_DEF(io, prefdir, 2),
MIST_FUNC_DEF(io, realdir, 1),
MIST_FUNC_DEF(io, open, 1),
MIST_FUNC_DEF(io, searchpath, 0),
MIST_FUNC_DEF(io, enumerate, 2),
MIST_FUNC_DEF(io, mount_core, 1),
MIST_FUNC_DEF(io, is_directory, 1),
};
static const JSCFunctionListEntry js_PHYSFS_File_funcs[] = {
MIST_FUNC_DEF(file, close, 0),
MIST_FUNC_DEF(file, write, 1),
MIST_FUNC_DEF(file, buffer, 1),
MIST_FUNC_DEF(file, tell, 0),
MIST_FUNC_DEF(file, eof, 0),
};
JSValue js_io_use(JSContext *js) {
QJSCLASSPREP_FUNCS(PHYSFS_File);
JSValue mod = JS_NewObject(js);
JS_SetPropertyFunctionList(js,mod,js_io_funcs,countof(js_io_funcs));
return mod;
}