diff --git a/src/file.js.c b/src/file.js.c index b7219522..e16708a1 100644 --- a/src/file.js.c +++ b/src/file.js.c @@ -4,6 +4,8 @@ #include "task.h" #include "util.js.h" +#include "unzip.h" + #include #include #include @@ -18,8 +20,10 @@ static JSValue _file_make_directory(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); static JSValue _file_remove_directory(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); static JSValue _file_read_file(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); +static JSValue _file_read_file_zip(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); static JSValue _file_rename_file(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); static JSValue _file_stat(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); +static JSValue _file_stat_zip(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); static JSValue _file_unlink_file(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); static JSValue _file_write_file(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv); @@ -44,14 +48,16 @@ void tf_file_register(JSContext* context) { JSValue global = JS_GetGlobalObject(context); JSValue file = JS_NewObject(context); + void* task = JS_GetContextOpaque(context); + const char* zip = tf_task_get_zip_path(task); JS_SetPropertyStr(context, global, "File", file); - JS_SetPropertyStr(context, file, "readFile", JS_NewCFunction(context, _file_read_file, "readFile", 1)); + JS_SetPropertyStr(context, file, "readFile", JS_NewCFunction(context, zip ? _file_read_file_zip : _file_read_file, "readFile", 1)); JS_SetPropertyStr(context, file, "writeFile", JS_NewCFunction(context, _file_write_file, "writeFile", 2)); JS_SetPropertyStr(context, file, "makeDirectory", JS_NewCFunction(context, _file_make_directory, "makeDirectory", 1)); JS_SetPropertyStr(context, file, "removeDirectory", JS_NewCFunction(context, _file_remove_directory, "removeDirectory", 1)); JS_SetPropertyStr(context, file, "unlinkFile", JS_NewCFunction(context, _file_unlink_file, "unlinkFile", 1)); JS_SetPropertyStr(context, file, "renameFile", JS_NewCFunction(context, _file_rename_file, "renameFile", 2)); - JS_SetPropertyStr(context, file, "stat", JS_NewCFunction(context, _file_stat, "stat", 1)); + JS_SetPropertyStr(context, file, "stat", JS_NewCFunction(context, zip ? _file_stat_zip : _file_stat, "stat", 1)); JS_FreeValue(context, global); } @@ -146,6 +152,112 @@ static JSValue _file_read_file(JSContext* context, JSValueConst this_val, int ar return promise_value; } +typedef struct _zip_read_work_t +{ + uv_work_t request; + JSContext* context; + tf_task_t* task; + const char* file_path; + promiseid_t promise; + int result; + uint8_t* buffer; + size_t size; +} zip_read_work_t; + +static void _file_read_file_zip_work(uv_work_t* work) +{ + zip_read_work_t* data = work->data; + unzFile zip = unzOpen(tf_task_get_zip_path(data->task)); + bool is_file_open = false; + if (!zip) + { + data->result = errno; + return; + } + + data->result = unzLocateFile(zip, data->file_path, 1); + if (data->result != UNZ_OK) + { + goto done; + } + + unz_file_info64 info = { 0 }; + data->result = unzGetCurrentFileInfo64(zip, &info, NULL, 0, NULL, 0, NULL, 0); + if (data->result != UNZ_OK) + { + goto done; + } + + data->result = unzOpenCurrentFile(zip); + if (data->result != UNZ_OK) + { + goto done; + } + is_file_open = true; + + data->buffer = tf_malloc(info.uncompressed_size); + data->result = unzReadCurrentFile(zip, data->buffer, info.uncompressed_size); + if (data->result <= 0) + { + tf_free(data->buffer); + data->buffer = NULL; + } + +done: + if (is_file_open) + { + unzCloseCurrentFile(zip); + } + unzClose(zip); +} + +static void _file_read_file_zip_after_work(uv_work_t* work, int status) +{ + zip_read_work_t* data = work->data; + if (data->result >= 0) + { + tf_task_resolve_promise(data->task, data->promise, tf_util_new_uint8_array(data->context, data->buffer, data->result)); + } + else + { + tf_task_reject_promise(data->task, data->promise, JS_ThrowInternalError(data->context, "Error: %d.", data->result)); + } + tf_free(data->buffer); + tf_free((void*)data->file_path); + tf_free(data); +} + +static JSValue _file_read_file_zip(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) +{ + void* task = JS_GetContextOpaque(context); + const char* file_name = JS_ToCString(context, argv[0]); + + zip_read_work_t* work = tf_malloc(sizeof(zip_read_work_t)); + *work = (zip_read_work_t) + { + .request = + { + .data = work, + }, + .context = context, + .task = task, + .file_path = tf_strdup(file_name), + }; + + JSValue promise_value = tf_task_allocate_promise(task, &work->promise); + + int r = uv_queue_work(tf_task_get_loop(task), &work->request, _file_read_file_zip_work, _file_read_file_zip_after_work); + if (r) + { + tf_task_reject_promise(task, work->promise, JS_ThrowInternalError(context, "%s", uv_strerror(r))); + tf_free((void*)work->file_path); + tf_free(work); + } + + JS_FreeCString(context, file_name); + return promise_value; +} + static void _file_write_write_callback(uv_fs_t* req) { uv_fs_req_cleanup(req); @@ -368,6 +480,29 @@ JSValue _file_stat(JSContext* context, JSValueConst this_val, int argc, JSValueC return promise_value; } +JSValue _file_stat_zip(JSContext* context, JSValueConst this_val, int argc, JSValueConst* argv) +{ + void* task = JS_GetContextOpaque(context); + promiseid_t promise = -1; + JSValue promise_value = tf_task_allocate_promise(task, &promise); + + file_stat_t* data = tf_malloc(sizeof(file_stat_t)); + data->_task = task; + data->_promise = promise; + data->_request.data = data; + data->_context = context; + + /* Ignore the requested path and stat the zip itself. */ + int result = uv_fs_stat(tf_task_get_loop(task), &data->_request, tf_task_get_zip_path(task), _file_on_stat_complete); + if (result) + { + tf_task_reject_promise(task, promise, JS_NewInt32(context, result)); + uv_fs_req_cleanup(&data->_request); + tf_free(data); + } + return promise_value; +} + static double _time_spec_to_double(const uv_timespec_t* time_spec) { return time_spec->tv_sec + (double)(time_spec->tv_nsec) / 1e9; diff --git a/src/task.c b/src/task.c index bd1f2317..ffedf34c 100644 --- a/src/task.c +++ b/src/task.c @@ -1882,6 +1882,11 @@ void tf_task_set_zip_path(tf_task_t* task, const char* zip_path) } } +const char* tf_task_get_zip_path(tf_task_t* task) +{ + return task->_zip ? task->_zip_path : NULL; +} + void tf_task_set_args(tf_task_t* task, const char* args) { task->_args = args; diff --git a/src/task.h b/src/task.h index d58f26e4..6dad97e9 100644 --- a/src/task.h +++ b/src/task.h @@ -42,6 +42,7 @@ void tf_task_set_http_port(tf_task_t* task, int port); void tf_task_set_https_port(tf_task_t* task, int port); void tf_task_set_db_path(tf_task_t* task, const char* path); void tf_task_set_zip_path(tf_task_t* task, const char* path); +const char* tf_task_get_zip_path(tf_task_t* task); void tf_task_set_args(tf_task_t* task, const char* args); void tf_task_activate(tf_task_t* task); void tf_task_run(tf_task_t* task);