forked from cory/tildefriends
		
	Refactored import and export. No user on disk.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@4164 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
		| @@ -257,12 +257,12 @@ static int _tf_command_export(const char* file, int argc, char* argv[]) | ||||
|  | ||||
| 	xoptOption options[] = { | ||||
| 		{ "db-path", 'd', offsetof(args_t, db_path), NULL, XOPT_TYPE_STRING, NULL, "Sqlite database path (default: db.sqlite)." }, | ||||
| 		{ "user", 'u', offsetof(args_t, user), NULL, XOPT_TYPE_STRING, NULL, "User into whose apps will be exported (default: \"cory\")." }, | ||||
| 		{ "user", 'u', offsetof(args_t, user), NULL, XOPT_TYPE_STRING, NULL, "User into whose apps will be exported (default: \"core\")." }, | ||||
| 		{ "help", 'h', offsetof(args_t, help), NULL, XOPT_TYPE_BOOL, NULL, "Shows this help message." }, | ||||
| 		XOPT_NULLOPTION, | ||||
| 	}; | ||||
|  | ||||
| 	args_t args = { .user = "cory" }; | ||||
| 	args_t args = { .user = "core" }; | ||||
| 	const char** extras = NULL; | ||||
| 	int extra_count = 0; | ||||
| 	const char *err = NULL; | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
| #include <sqlite3.h> | ||||
| #include <uv.h> | ||||
|  | ||||
| static void _write_file(const char* path, void* blob, size_t size) | ||||
| static void _write_file(const char* path, const void* blob, size_t size) | ||||
| { | ||||
| 	FILE* file = fopen(path, "wb"); | ||||
| 	if (file) | ||||
| @@ -35,6 +35,45 @@ static void _make_dir(const char* path) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| typedef struct _tf_export_t | ||||
| { | ||||
| 	tf_ssb_t* ssb; | ||||
| 	const char* parent; | ||||
| 	JSValue files; | ||||
| 	uv_fs_t req; | ||||
| 	bool done; | ||||
| } tf_export_t; | ||||
|  | ||||
| static void _tf_ssb_export_scandir(uv_fs_t* req) | ||||
| { | ||||
| 	tf_export_t* export = req->data; | ||||
| 	JSContext* context = tf_ssb_get_context(export->ssb); | ||||
| 	uv_dirent_t ent; | ||||
| 	while (uv_fs_scandir_next(req, &ent) == 0) | ||||
| 	{ | ||||
| 		if (ent.type == UV_DIRENT_FILE) | ||||
| 		{ | ||||
| 			JSValue found = JS_GetPropertyStr(context, export->files, ent.name); | ||||
| 			if (JS_IsUndefined(found)) | ||||
| 			{ | ||||
| 				size_t len = strlen(export->parent) + strlen(ent.name) + 2; | ||||
| 				char* path = tf_malloc(len); | ||||
| 				snprintf(path, len, "%s/%s", export->parent, ent.name); | ||||
| 				uv_fs_t req = { 0 }; | ||||
| 				int r = uv_fs_unlink(tf_ssb_get_loop(export->ssb), &req, path, NULL); | ||||
| 				if (r) | ||||
| 				{ | ||||
| 					printf("Failed to unlink %s: %s.", path, uv_strerror(r)); | ||||
| 				} | ||||
| 				uv_fs_req_cleanup(&req); | ||||
| 				tf_free(path); | ||||
| 			} | ||||
| 			JS_FreeValue(context, found); | ||||
| 		} | ||||
| 	} | ||||
| 	export->done = true; | ||||
| } | ||||
|  | ||||
| void tf_ssb_export(tf_ssb_t* ssb, const char* key) | ||||
| { | ||||
| 	char user[256] = { 0 }; | ||||
| @@ -80,12 +119,9 @@ void tf_ssb_export(tf_ssb_t* ssb, const char* key) | ||||
| 	} | ||||
| 	char file_path[1024]; | ||||
| 	_make_dir("apps/"); | ||||
| 	snprintf(file_path, sizeof(file_path), "apps/%s", user); | ||||
| 	snprintf(file_path, sizeof(file_path), "apps/%s", path); | ||||
| 	_make_dir(file_path); | ||||
| 	snprintf(file_path, sizeof(file_path), "apps/%s/%s", user, path); | ||||
| 	_make_dir(file_path); | ||||
| 	snprintf(file_path, sizeof(file_path), "apps/%s/%s.json", user, path); | ||||
| 	_write_file(file_path, blob, size); | ||||
| 	snprintf(file_path, sizeof(file_path), "apps/%s.json", path); | ||||
| 	JSContext* context = tf_ssb_get_context(ssb); | ||||
| 	JSValue app = JS_ParseJSON(context, (const char*)blob, size, NULL); | ||||
| 	tf_free(blob); | ||||
| @@ -108,7 +144,7 @@ void tf_ssb_export(tf_ssb_t* ssb, const char* key) | ||||
| 				size_t file_size = 0; | ||||
| 				if (tf_ssb_db_blob_get(ssb, blob_id, &file_blob, &file_size)) | ||||
| 				{ | ||||
| 					snprintf(file_path, sizeof(file_path), "apps/%s/%s/%s", user, path, file_name); | ||||
| 					snprintf(file_path, sizeof(file_path), "apps/%s/%s", path, file_name); | ||||
| 					_write_file(file_path, file_blob, file_size); | ||||
| 					tf_free(file_blob); | ||||
| 				} | ||||
| @@ -129,6 +165,40 @@ void tf_ssb_export(tf_ssb_t* ssb, const char* key) | ||||
| 	} | ||||
| 	js_free(context, ptab); | ||||
|  | ||||
| 	JSAtom files_atom = JS_NewAtom(context, "files"); | ||||
| 	JS_DeleteProperty(context, app, files_atom, 0); | ||||
| 	JS_FreeAtom(context, files_atom); | ||||
|  | ||||
| 	JSValue json = JS_JSONStringify(context, app, JS_NULL, JS_NewInt32(context, 2)); | ||||
| 	size_t length = 0; | ||||
| 	const char* string = JS_ToCStringLen(context, &length, json); | ||||
| 	snprintf(file_path, sizeof(file_path), "apps/%s.json", path); | ||||
| 	_write_file(file_path, string, length); | ||||
| 	JS_FreeCString(context, string); | ||||
| 	JS_FreeValue(context, json); | ||||
|  | ||||
| 	snprintf(file_path, sizeof(file_path), "apps//%s", path); | ||||
| 	tf_export_t export = | ||||
| 	{ | ||||
| 		.parent = file_path, | ||||
| 		.ssb = ssb, | ||||
| 		.files = files, | ||||
| 		.req = | ||||
| 		{ | ||||
| 			.data = &export, | ||||
| 		}, | ||||
| 	}; | ||||
| 	int r = uv_fs_scandir(tf_ssb_get_loop(ssb), &export.req, file_path, 0, _tf_ssb_export_scandir); | ||||
| 	if (r) | ||||
| 	{ | ||||
| 		printf("Failed to scan directory %s: %s.", file_path, uv_strerror(r)); | ||||
| 	} | ||||
| 	while (!export.done) | ||||
| 	{ | ||||
| 		uv_run(tf_ssb_get_loop(ssb), UV_RUN_ONCE); | ||||
| 	} | ||||
| 	uv_fs_req_cleanup(&export.req); | ||||
|  | ||||
| 	JS_FreeValue(context, files); | ||||
| 	JS_FreeValue(context, app); | ||||
| } | ||||
|   | ||||
							
								
								
									
										266
									
								
								src/ssb.import.c
									
									
									
									
									
								
							
							
						
						
									
										266
									
								
								src/ssb.import.c
									
									
									
									
									
								
							| @@ -12,25 +12,6 @@ | ||||
| #include <sqlite3.h> | ||||
| #include <uv.h> | ||||
|  | ||||
| typedef struct _tf_import_file_t { | ||||
| 	uv_fs_t req; | ||||
| 	uv_file file; | ||||
| 	tf_ssb_t* ssb; | ||||
| 	const char* user; | ||||
| 	const char* parent; | ||||
| 	const char* name; | ||||
| 	char data[k_ssb_blob_bytes_max]; | ||||
| 	int* work_left; | ||||
| } tf_import_file_t; | ||||
|  | ||||
| static void _tf_ssb_import_file_close(uv_fs_t* req) | ||||
| { | ||||
| 	tf_import_file_t* file = req->data; | ||||
| 	(*file->work_left)--; | ||||
| 	uv_fs_req_cleanup(req); | ||||
| 	tf_free(req->data); | ||||
| } | ||||
|  | ||||
| static void _tf_ssb_import_add_app(tf_ssb_t* ssb, const char* user, const char* app) | ||||
| { | ||||
| 	sqlite3_stmt* statement; | ||||
| @@ -80,6 +61,7 @@ static void _tf_ssb_import_add_app(tf_ssb_t* ssb, const char* user, const char* | ||||
| 		JS_FreeCString(context, last_added); | ||||
| 		last_added = read_string; | ||||
| 	} | ||||
| 	JS_FreeCString(context, last_added); | ||||
|  | ||||
| 	JSValue json = JS_JSONStringify(context, out_apps, JS_NULL, JS_NULL); | ||||
| 	const char* text = JS_ToCString(context, json); | ||||
| @@ -99,6 +81,7 @@ static void _tf_ssb_import_add_app(tf_ssb_t* ssb, const char* user, const char* | ||||
| 	JS_FreeValue(context, apps); | ||||
| } | ||||
|  | ||||
| /* | ||||
| static void _tf_ssb_import_file_read(uv_fs_t* req) | ||||
| { | ||||
| 	tf_import_file_t* file = req->data; | ||||
| @@ -106,32 +89,49 @@ static void _tf_ssb_import_file_read(uv_fs_t* req) | ||||
| 	if (req->result >= 0) | ||||
| 	{ | ||||
| 		bool is_new = false; | ||||
| 		if (tf_ssb_db_blob_store(file->ssb, (const uint8_t*)file->data, req->result, id, sizeof(id), &is_new)) | ||||
| 		bool is_app_json = false; | ||||
|  | ||||
| 		if (strcasecmp(file->name + strlen(file->name) - strlen(".json"), ".json") == 0) | ||||
| 		{ | ||||
| 			if (is_new) | ||||
| 			JSContext* context = tf_ssb_get_context(file->ssb); | ||||
| 			JSValue object = JS_ParseJSON(context, file->data, req->result, NULL); | ||||
| 			JSValue type = JS_GetPropertyStr(context, object, "type"); | ||||
| 			if (!JS_IsUndefined(type)) | ||||
| 			{ | ||||
| 				printf("Stored %s/%s as %s.\n", file->parent, file->name, id); | ||||
| 			} | ||||
| 			if (strcasecmp(file->name + strlen(file->name) - strlen(".json"), ".json") == 0) | ||||
| 			{ | ||||
| 				sqlite3_stmt* statement; | ||||
| 				if (sqlite3_prepare(tf_ssb_get_db(file->ssb), "INSERT OR REPLACE INTO properties (id, key, value) VALUES ($1, 'path:' || $2, $3)", -1, &statement, NULL) == SQLITE_OK) | ||||
| 				const char* type_string = JS_ToCString(context, type); | ||||
| 				if (type_string && strcmp(type_string, "tf-application") == 0) | ||||
| 				{ | ||||
| 					((char*)file->name)[strlen(file->name) - strlen(".json")] = '\0'; | ||||
| 					if (sqlite3_bind_text(statement, 1, file->user, -1, NULL) == SQLITE_OK && | ||||
| 						sqlite3_bind_text(statement, 2, file->name, -1, NULL) == SQLITE_OK && | ||||
| 						sqlite3_bind_text(statement, 3, id, -1, NULL) == SQLITE_OK && | ||||
| 						sqlite3_step(statement) == SQLITE_DONE) | ||||
| 					is_app_json = true; | ||||
|  | ||||
| 					sqlite3_stmt* statement; | ||||
| 					if (sqlite3_prepare(tf_ssb_get_db(file->ssb), "INSERT OR REPLACE INTO properties (id, key, value) VALUES ($1, 'path:' || $2, $3)", -1, &statement, NULL) == SQLITE_OK) | ||||
| 					{ | ||||
| 						if (sqlite3_changes(tf_ssb_get_db(file->ssb))) | ||||
| 						((char*)file->name)[strlen(file->name) - strlen(".json")] = '\0'; | ||||
| 						if (sqlite3_bind_text(statement, 1, file->user, -1, NULL) == SQLITE_OK && | ||||
| 							sqlite3_bind_text(statement, 2, file->name, -1, NULL) == SQLITE_OK && | ||||
| 							sqlite3_bind_text(statement, 3, id, -1, NULL) == SQLITE_OK && | ||||
| 							sqlite3_step(statement) == SQLITE_DONE) | ||||
| 						{ | ||||
| 							printf("Registered %s path:%s as %s.\n", file->user, file->name, id); | ||||
| 							if (sqlite3_changes(tf_ssb_get_db(file->ssb))) | ||||
| 							{ | ||||
| 								printf("Registered %s path:%s as %s.\n", file->user, file->name, id); | ||||
| 							} | ||||
| 							_tf_ssb_import_add_app(file->ssb, file->user, file->name); | ||||
| 						} | ||||
| 						_tf_ssb_import_add_app(file->ssb, file->user, file->name); | ||||
| 						sqlite3_finalize(statement); | ||||
| 					} | ||||
| 					sqlite3_finalize(statement); | ||||
| 				} | ||||
| 				JS_FreeCString(context, type_string); | ||||
| 			} | ||||
| 			JS_FreeValue(context, object); | ||||
|  | ||||
| 		} | ||||
|  | ||||
| 		if (!is_app_json && | ||||
| 			tf_ssb_db_blob_store(file->ssb, (const uint8_t*)file->data, req->result, id, sizeof(id), &is_new) && | ||||
| 			is_new) | ||||
| 		{ | ||||
| 			printf("Stored %s/%s as %s.\n", file->parent, file->name, id); | ||||
| 		} | ||||
| 	} | ||||
| 	uv_fs_req_cleanup(req); | ||||
| @@ -146,74 +146,178 @@ static void _tf_ssb_import_file_open(uv_fs_t* req) | ||||
| 	uv_fs_read(tf_ssb_get_loop(file->ssb), req, file->file, &(uv_buf_t) { .base = file->data, .len = k_ssb_blob_bytes_max }, 1, 0, _tf_ssb_import_file_read); | ||||
| } | ||||
|  | ||||
| typedef struct _tf_import_t { | ||||
| static void _tf_ssb_import_register_app(tf_ssb_t* ssb, const char* use, const char* app, const char* id) | ||||
| { | ||||
| 	sqlite3_stmt* statement; | ||||
| 	if (sqlite3_prepare(tf_ssb_get_db(file->ssb), "INSERT OR REPLACE INTO properties (id, key, value) VALUES ($1, 'path:' || $2, $3)", -1, &statement, NULL) == SQLITE_OK) | ||||
| 	{ | ||||
| 		((char*)file->name)[strlen(file->name) - strlen(".json")] = '\0'; | ||||
| 		if (sqlite3_bind_text(statement, 1, file->user, -1, NULL) == SQLITE_OK && | ||||
| 			sqlite3_bind_text(statement, 2, file->name, -1, NULL) == SQLITE_OK && | ||||
| 			sqlite3_bind_text(statement, 3, id, -1, NULL) == SQLITE_OK && | ||||
| 			sqlite3_step(statement) == SQLITE_DONE) | ||||
| 		{ | ||||
| 			if (sqlite3_changes(tf_ssb_get_db(file->ssb))) | ||||
| 			{ | ||||
| 				printf("Registered %s path:%s as %s.\n", file->user, file->name, id); | ||||
| 			} | ||||
| 			_tf_ssb_import_add_app(file->ssb, file->user, file->name); | ||||
| 		} | ||||
| 		sqlite3_finalize(statement); | ||||
| 	} | ||||
| } | ||||
| */ | ||||
|  | ||||
| typedef struct _tf_import_t | ||||
| { | ||||
| 	tf_ssb_t* ssb; | ||||
| 	const char* user; | ||||
| 	const char* parent; | ||||
| 	uv_fs_t req; | ||||
| 	int work_left; | ||||
| } tf_import_t; | ||||
|  | ||||
| static void _tf_ssb_import_scandir(uv_fs_t* req) | ||||
| static char* _tf_ssb_import_read_file(uv_loop_t* loop, const char* path, size_t* out_size) | ||||
| { | ||||
| 	tf_import_t* import = req->data; | ||||
| 	uv_dirent_t ent; | ||||
| 	while (uv_fs_scandir_next(req, &ent) == 0) | ||||
| 	char* data = NULL; | ||||
| 	uv_fs_t req = { 0 }; | ||||
| 	int handle = uv_fs_open(loop, &req, path, 0, 0, NULL); | ||||
| 	if (handle >= 0) | ||||
| 	{ | ||||
| 		size_t len = strlen(import->parent) + strlen(ent.name) + 2; | ||||
| 		char* path = tf_malloc(len); | ||||
| 		snprintf(path, len, "%s/%s", import->parent, ent.name); | ||||
| 		if (ent.type == UV_DIRENT_DIR) | ||||
| 		uv_fs_t read_req = { 0 }; | ||||
| 		data = tf_malloc(k_ssb_blob_bytes_max); | ||||
| 		int r = uv_fs_read(loop, &read_req, handle, &(uv_buf_t) { .base = data, .len = k_ssb_blob_bytes_max }, 1, 0, NULL); | ||||
| 		if (r >= 0 && r < k_ssb_blob_bytes_max) | ||||
| 		{ | ||||
| 			tf_ssb_import(import->ssb, import->user, path); | ||||
| 			data[r] = '\0'; | ||||
| 			*out_size = r; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			size_t size = sizeof(tf_import_file_t) + strlen(import->parent) +1 + strlen(ent.name) + 1; | ||||
| 			tf_import_file_t* file = tf_malloc(size); | ||||
| 			memset(file, 0, size); | ||||
| 			file->ssb = import->ssb; | ||||
| 			file->user = import->user; | ||||
| 			file->parent = (void*)(file + 1); | ||||
| 			file->name = file->parent + strlen(import->parent) + 1; | ||||
| 			file->req.data = file; | ||||
| 			file->work_left = &import->work_left; | ||||
| 			memcpy((char*)file->parent, import->parent, strlen(import->parent) + 1); | ||||
| 			memcpy((char*)file->name, ent.name, strlen(ent.name) + 1); | ||||
| 			printf("Failed to read %s: %s.\n", path, uv_strerror(r)); | ||||
| 		} | ||||
| 		uv_fs_req_cleanup(&read_req); | ||||
|  | ||||
| 			import->work_left++; | ||||
| 			int r = uv_fs_open(tf_ssb_get_loop(import->ssb), &file->req, path, 0, 0, _tf_ssb_import_file_open); | ||||
| 			if (r < 0) | ||||
| 		uv_fs_t close_req = { 0 }; | ||||
| 		r = uv_fs_close(loop, &close_req, handle, NULL); | ||||
| 		if (r) | ||||
| 		{ | ||||
| 			printf("Failed to close %s: %s.\n", path, uv_strerror(r)); | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		printf("Failed to open %s: %s.\n", path, uv_strerror(handle)); | ||||
| 	} | ||||
| 	uv_fs_req_cleanup(&req); | ||||
| 	return data; | ||||
| } | ||||
|  | ||||
| static void _tf_ssb_import_recursive_add_files(tf_ssb_t* ssb, uv_loop_t* loop, JSContext* context, JSValue files, const char* root, const char* path) | ||||
| { | ||||
| 	uv_fs_t req = { 0 }; | ||||
| 	int r = uv_fs_scandir(loop, &req, path, 0, NULL); | ||||
| 	if (r >= 0) | ||||
| 	{ | ||||
| 		uv_dirent_t ent; | ||||
| 		while (uv_fs_scandir_next(&req, &ent) == 0) | ||||
| 		{ | ||||
| 			if (ent.type == UV_DIRENT_FILE) | ||||
| 			{ | ||||
| 				printf("Failed to open %s: %s.\n", path, uv_strerror(r)); | ||||
| 				tf_free(file); | ||||
| 				import->work_left--; | ||||
| 				size_t len = strlen(path) + strlen(ent.name) + 2; | ||||
| 				char* full_path = tf_malloc(len); | ||||
| 				snprintf(full_path, len, "%s/%s", path, ent.name); | ||||
|  | ||||
| 				size_t size = 0; | ||||
| 				char* blob = _tf_ssb_import_read_file(loop, full_path, &size); | ||||
| 				char id[k_id_base64_len] = { 0 }; | ||||
| 				bool is_new = false; | ||||
| 				if (tf_ssb_db_blob_store(ssb, (const uint8_t*)blob, size, id, sizeof(id), &is_new) && is_new) | ||||
| 				{ | ||||
| 					printf("Stored %s as %s.\n", full_path, id); | ||||
| 				} | ||||
| 				JS_SetPropertyStr(context, files, full_path + strlen(root) + 1, JS_NewString(context, id)); | ||||
|  | ||||
| 				tf_free(blob); | ||||
| 				tf_free(full_path); | ||||
| 			} | ||||
| 		} | ||||
| 		tf_free(path); | ||||
| 	} | ||||
| 	import->work_left--; | ||||
| 	else | ||||
| 	{ | ||||
| 		printf("Failed to scan directory %s: %s.", path, uv_strerror(r)); | ||||
| 	} | ||||
| 	uv_fs_req_cleanup(&req); | ||||
| } | ||||
|  | ||||
| static void _tf_ssb_import_app_json(tf_ssb_t* ssb, uv_loop_t* loop, JSContext* context, const char* user, const char* path) | ||||
| { | ||||
| 	uv_fs_t req = { 0 }; | ||||
| 	int r = uv_fs_open(loop, &req, path, 0, 0, NULL); | ||||
| 	if (r >= 0) | ||||
| 	{ | ||||
| 		size_t size = 0; | ||||
| 		char* file = _tf_ssb_import_read_file(loop, path, &size); | ||||
| 		if (file) | ||||
| 		{ | ||||
| 			JSValue app = JS_ParseJSON(context, file, size, NULL); | ||||
| 			if (!tf_util_report_error(context, app)) | ||||
| 			{ | ||||
| 				char* dir = tf_strdup(path); | ||||
| 				dir[strlen(dir) - strlen(".json")] = '\0'; | ||||
| 				JSValue files = JS_NewObject(context); | ||||
| 				_tf_ssb_import_recursive_add_files(ssb, loop, context, files, dir, dir); | ||||
| 				JS_SetPropertyStr(context, app, "files", files); | ||||
|  | ||||
| 				JSValue json = JS_JSONStringify(context, app, JS_NULL, JS_NULL); | ||||
| 				size_t size = 0; | ||||
| 				const char* blob = JS_ToCStringLen(context, &size, json); | ||||
| 				char id[k_id_base64_len] = { 0 }; | ||||
| 				if (tf_ssb_db_blob_store(ssb, (const uint8_t*)blob, size, id, sizeof(id), NULL)) | ||||
| 				{ | ||||
| 					const char* app = dir; | ||||
| 					char* slash = strrchr(dir, '/'); | ||||
| 					app = slash ? slash + 1 : app; | ||||
| 					_tf_ssb_import_add_app(ssb, user, app); | ||||
| 				} | ||||
| 				JS_FreeCString(context, blob); | ||||
| 				JS_FreeValue(context, json); | ||||
|  | ||||
| 				tf_free(dir); | ||||
| 			} | ||||
| 			JS_FreeValue(context, app); | ||||
| 			tf_free(file); | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		printf("Failed to open %s: %s.\n", path, uv_strerror(r)); | ||||
| 	} | ||||
| 	uv_fs_req_cleanup(&req); | ||||
| } | ||||
|  | ||||
| void tf_ssb_import(tf_ssb_t* ssb, const char* user, const char* path) | ||||
| { | ||||
| 	tf_import_t import = { | ||||
| 		.ssb = ssb, | ||||
| 		.user = user, | ||||
| 		.parent = path, | ||||
| 		.work_left = 1, | ||||
| 	}; | ||||
| 	import.req.data = &import; | ||||
| 	uv_fs_t req = { 0 }; | ||||
| 	sqlite3_busy_timeout(tf_ssb_get_db(ssb), 10000); | ||||
| 	int r = uv_fs_scandir(tf_ssb_get_loop(ssb), &import.req, path, 0, _tf_ssb_import_scandir); | ||||
| 	if (r) | ||||
| 	int r = uv_fs_scandir(tf_ssb_get_loop(ssb), &req, path, 0, NULL); | ||||
| 	if (r >= 0) | ||||
| 	{ | ||||
| 		uv_dirent_t ent; | ||||
| 		while (uv_fs_scandir_next(&req, &ent) == 0) | ||||
| 		{ | ||||
| 			size_t len = strlen(path) + strlen(ent.name) + 2; | ||||
| 			char* full_path = tf_malloc(len); | ||||
| 			snprintf(full_path, len, "%s/%s", path, ent.name); | ||||
| 			if (strcasecmp(ent.name + strlen(ent.name) - strlen(".json"), ".json") == 0) | ||||
| 			{ | ||||
| 				_tf_ssb_import_app_json(ssb, tf_ssb_get_loop(ssb), tf_ssb_get_context(ssb), user, full_path); | ||||
| 			} | ||||
| 			tf_free(full_path); | ||||
| 		} | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		printf("Failed to scan directory %s: %s.", path, uv_strerror(r)); | ||||
| 	} | ||||
|  | ||||
| 	while (import.work_left > 0) | ||||
| 	{ | ||||
| 		uv_run(tf_ssb_get_loop(ssb), UV_RUN_ONCE); | ||||
| 	} | ||||
| 	uv_fs_req_cleanup(&import.req); | ||||
| 	uv_fs_req_cleanup(&req); | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user