Some checks failed
Build and Deploy / build-macos (push) Failing after 7s
Build and Deploy / build-linux (push) Has been cancelled
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
393 lines
10 KiB
C
393 lines
10 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_CCALL(io_mount,
|
|
const char *src = JS_ToCString(js, argv[0]);
|
|
const char *mountpoint = JS_ToCString(js, argv[1]);
|
|
int prepend = 0;
|
|
|
|
if (argc > 2 && !JS_IsUndefined(argv[2]))
|
|
prepend = JS_ToBool(js, argv[2]);
|
|
|
|
if (!PHYSFS_mount(src, mountpoint, prepend))
|
|
ret = JS_ThrowReferenceError(js,"Unable to mount %s at %s: %s", src, mountpoint, PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
|
|
|
|
JS_FreeCString(js, src);
|
|
JS_FreeCString(js, mountpoint);
|
|
)
|
|
|
|
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, 3),
|
|
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;
|
|
}
|