Implement enough of the File JS API to serve some web pages.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4208 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
		
							
								
								
									
										139
									
								
								src/file.js.c
									
									
									
									
									
								
							
							
						
						
									
										139
									
								
								src/file.js.c
									
									
									
									
									
								
							| @@ -4,6 +4,8 @@ | ||||
| #include "task.h" | ||||
| #include "util.js.h" | ||||
|  | ||||
| #include "unzip.h" | ||||
|  | ||||
| #include <stdbool.h> | ||||
| #include <string.h> | ||||
| #include <uv.h> | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -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); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user