bu5hm4n pushed a commit to branch master. http://git.enlightenment.org/apps/extra.git/commit/?id=7f1424b2d2775059677d5d2810757112a31d6adc
commit 7f1424b2d2775059677d5d2810757112a31d6adc Author: Marcel Hollerbach <marcel-hollerb...@t-online.de> Date: Tue Jan 17 15:37:43 2017 +0100 extra: this refactors the json parsing This refactors the json parsing into its single file, the parsing is now much lesser work. It also adds the downloading of the available background images in the sync call --- src/lib/Makefile.am | 1 + src/lib/extra.c | 306 +++++++++++++++++++++++---------------------- src/lib/extra.h | 8 ++ src/lib/extra_api_helper.c | 192 ++++++++++++++++++++++++++++ src/lib/extra_private.h | 25 ++++ 5 files changed, 385 insertions(+), 147 deletions(-) diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am index 07f765f..6281abe 100644 --- a/src/lib/Makefile.am +++ b/src/lib/Makefile.am @@ -14,6 +14,7 @@ includes_HEADERS = extra.h includesdir = $(includedir)/extra-@VMAJ@ libextra_la_SOURCES = extra.c \ + extra_api_helper.c \ jsmn/jsmn.c \ jsmn/jsmn.h diff --git a/src/lib/extra.c b/src/lib/extra.c index 4678e26..6e8cb85 100644 --- a/src/lib/extra.c +++ b/src/lib/extra.c @@ -6,7 +6,6 @@ #include <fcntl.h> #include "extra.h" -#include "jsmn/jsmn.h" #include "extra_private.h" static int _extra_init = 0; @@ -14,7 +13,8 @@ static Ecore_Event_Handler *_data; static Ecore_Event_Handler *_complete; int _extra_lib_log_dom = -1; -Eina_List *_theme_list; +Eina_List *_theme_list = NULL; +Eina_List *_background_list = NULL; #define sec_strdup(v) v ? eina_strbuf_string_steal(v) : NULL @@ -25,6 +25,9 @@ void _extra_theme_cache_load(); #define GEN_FILE_NAME(buf, t) eina_strbuf_append_printf(buf, "%s-%d.edj", t->id, t->version); +//=========== +//theme stuff + typedef struct { Extra_Theme theme; char state; //indicates if some downloads are in progress @@ -36,24 +39,100 @@ typedef struct { char nand_mask; //will be applied to the status char of the theme } Extra_Download_Job; +typedef struct { + Eina_Strbuf *description; + Eina_Strbuf *author; + Eina_Strbuf *name; + Eina_Strbuf *version; + Eina_Strbuf *id; +} Theme_Object; -static void -_extra_theme_add(Eina_Strbuf *id, Eina_Strbuf *name, - Eina_Strbuf *author, Eina_Strbuf *description, - int version) +static Eina_Bool +_fill_themes(Eina_Strbuf *buf) { - Extra_Theme_Private *theme; + Theme_Object *obj; + + eina_list_free(_theme_list); + _theme_list = NULL; + + EXTRA_JSON_TO_LIST_TEMPLATE_INIT(template, Theme_Object, + EXTRA_JSON_STRUCT_FIELD("description", Theme_Object, description, EINA_FALSE), + EXTRA_JSON_STRUCT_FIELD("author", Theme_Object, author, EINA_FALSE), + EXTRA_JSON_STRUCT_FIELD("name", Theme_Object, name, EINA_TRUE), + EXTRA_JSON_STRUCT_FIELD("version", Theme_Object, version, EINA_TRUE), + EXTRA_JSON_STRUCT_FIELD("theme_id", Theme_Object, id, EINA_TRUE) + ); + + Eina_List *lst = extra_json_to_list(&template, buf); + + EINA_LIST_FREE(lst, obj) + { + Extra_Theme_Private *theme; + int versionNumb; - theme = malloc(sizeof(*theme)); - theme->theme.id = sec_strdup(id); - theme->theme.name = sec_strdup(name); - theme->theme.author = sec_strdup(author); - theme->theme.description = sec_strdup(description); - theme->theme.version = version; - theme->state = 0; - _theme_list = eina_list_append(_theme_list, theme); + versionNumb = atoi(eina_strbuf_string_get(obj->version)); + + theme = malloc(sizeof(*theme)); + theme->theme.id = sec_strdup(obj->id); + theme->theme.name = sec_strdup(obj->name); + theme->theme.author = sec_strdup(obj->author); + theme->theme.description = sec_strdup(obj->description); + theme->theme.version = versionNumb; + theme->state = 0; + _theme_list = eina_list_append(_theme_list, theme); + + extra_json_list_part_free(&template, obj); + } + + return EINA_TRUE; } + +//================ +//background stuff + +typedef struct { + Eina_Strbuf *author; + Eina_Strbuf *name; + Eina_Strbuf *version; + Eina_Strbuf *id; +} Background_Object; + +static Eina_Bool +_fill_backgrounds(Eina_Strbuf *buf) +{ + Background_Object *obj; + + EXTRA_JSON_TO_LIST_TEMPLATE_INIT(template, Background_Object, + EXTRA_JSON_STRUCT_FIELD("author", Background_Object, author, EINA_FALSE), + EXTRA_JSON_STRUCT_FIELD("name", Background_Object, name, EINA_TRUE), + EXTRA_JSON_STRUCT_FIELD("version", Background_Object, version, EINA_TRUE), + EXTRA_JSON_STRUCT_FIELD("background-id", Background_Object, id, EINA_TRUE), + ); + + Eina_List *lst = extra_json_to_list(&template, buf); + + EINA_LIST_FREE(lst, obj) + { + Extra_Background *background = calloc(1, sizeof(Extra_Background)); + int versionNumb; + + versionNumb = atoi(eina_strbuf_string_get(obj->version)); + + background->author = sec_strdup(obj->author); + background->id = sec_strdup(obj->id); + background->name = sec_strdup(obj->name); + background->version = versionNumb; + + _background_list = eina_list_append(_background_list, background); + + extra_json_list_part_free(&template, obj); + } + + return EINA_TRUE; +} + + EAPI int extra_init(void) { @@ -125,173 +204,107 @@ _url_data_cb(void *data EINA_UNUSED, int type EINA_UNUSED, void *event_info) return EINA_TRUE; } -static int -_string_tuple_get(Eina_Strbuf *c, jsmntok_t *array, int i, Eina_Strbuf **name, Eina_Strbuf **value) +static char * +_cache_path_get(const char *purpose) { - if (array[i].type != JSMN_STRING || array[i].size != 1) - { - printf("expected string type with children\n"); - return 0; - } - - *name = eina_strbuf_substr_get(c, array[i].start, array[i].end - array[i].start); + char *path; - if ((array[i + 1].type != JSMN_STRING && array[i + 1].type != JSMN_PRIMITIVE) || array[i + 1].size != 0) - { - printf("Expected string type without children\n"); - return 0; - } - *value = eina_strbuf_substr_get(c, array[i + 1].start, array[i + 1].end - array[i + 1].start); + path = malloc(PATH_MAX * sizeof(char)); + sprintf(path, "%s/%s/%s.json", efreet_cache_home_get(), PACKAGE_NAME, purpose); - return 2; + return path; } -static Eina_Bool -_fill_themes(Eina_Strbuf *buf) +static void +_cache_content(Eina_Strbuf *content, const char *purpose) { - jsmn_parser parser; - jsmntok_t parts[201]; - int n, c = 0; - - jsmn_init(&parser); + char *path; + FILE *cache; - const char *string = eina_strbuf_string_get(buf); - n = jsmn_parse(&parser, string, strlen(string), parts, 201); - if (n == 0) - { - printf("No themes received\n"); - return EINA_FALSE; - } + path = _cache_path_get(purpose); - if (parts[0].type != JSMN_OBJECT) + cache = fopen(path, "w+"); + if (!cache) + ERR("Failed to open cache."); + else { - printf("Root node should be a object\n"); - return EINA_FALSE; + fprintf(cache, "%s", eina_strbuf_string_get(content)); + fclose(cache); } - c += 1; - - eina_list_free(_theme_list); - _theme_list = NULL; - - for (int i = 0; i < parts[0].size; ++i) - { - Eina_Strbuf *id = NULL, *name = NULL, *version = NULL, *description = NULL, *author = NULL; - int versionNumb; - //expect string object tuple - if (parts[c].type != JSMN_STRING || parts[c].size != 1) - { - printf("Expected String type with one child\n"); - return EINA_FALSE; - } - c += 1; - - if (parts[c].type != JSMN_OBJECT || parts[c].size <= 0) - { - printf("Expected Object type with more than 0 children\n"); - return EINA_FALSE; - } - - int max = parts[c].size; - c+=1; - for (int i2 = 0; i2 < max; ++i2) - { - Eina_Strbuf *value = NULL, *property = NULL; - const char *v; - int j = _string_tuple_get(buf, parts, c, &value, &property); - - if (!j) return EINA_FALSE; - c += j; - - v = eina_strbuf_string_steal(value); - - if (!strcmp(v, "description")) - description = property; - else if (!strcmp(v, "author")) - author = property; - else if (!strcmp(v, "name")) - name = property; - else if (!strcmp(v, "version")) - version = property; - else if (!strcmp(v, "theme_id")) - id = property; - else - eina_strbuf_free(property); - eina_strbuf_free(value); - - } - - versionNumb = atoi(eina_strbuf_string_steal(version)); + free(path); +} - _extra_theme_add(id, name, author, description, versionNumb); +typedef struct { + Extra_Progress *progress; + Ecore_Con_Url *themes; + Eina_Strbuf *themes_content; + Ecore_Con_Url *backgrounds; + Eina_Strbuf *background_content; +} Extra_Sync_Request; - eina_strbuf_free(id); - eina_strbuf_free(name); - eina_strbuf_free(author); - eina_strbuf_free(description); - eina_strbuf_free(version); - } +static void +_extra_sync_request_end_eval(Extra_Sync_Request *req) +{ + if (!req->themes_content || !req->background_content) return; - return EINA_TRUE; -} + if (_fill_themes(req->themes_content)) + _cache_content(req->themes_content, "themes"); -static char * -_theme_cache_path_get() -{ - char *path; + if (_fill_backgrounds(req->background_content)) + _cache_content(req->background_content, "backgrounds"); - path = malloc(PATH_MAX * sizeof(char)); - sprintf(path, "%s/%s/%s.json", efreet_cache_home_get(), PACKAGE_NAME, "themes"); + if (req->progress->done_cb) + req->progress->done_cb(req->progress->data); - return path; + ecore_event_handler_del(_data); + ecore_event_handler_del(_complete); } static Eina_Bool _url_complete_cb(void *data, int type EINA_UNUSED, void *event_info) { - Extra_Progress *progress = data;; Ecore_Con_Event_Url_Complete *complete = event_info; - Eina_Strbuf *buf; - Eina_Bool parsed; - - buf = ecore_con_url_data_get(complete->url_con); - parsed = _fill_themes(buf); + Extra_Sync_Request *request = data; - if (parsed) + if (complete->url_con == request->backgrounds) { - FILE *cache; - char *cache_path = _theme_cache_path_get(); - const char *content = eina_strbuf_string_get(buf); - - cache = fopen(cache_path, "w+"); - fprintf(cache, "%s", content); - fclose(cache); - free(cache_path); + request->background_content = ecore_con_url_data_get(complete->url_con); + _extra_sync_request_end_eval(request); + } + else if (complete->url_con == request->themes) + { + request->themes_content = ecore_con_url_data_get(complete->url_con); + _extra_sync_request_end_eval(request); } - - if (progress->done_cb) - progress->done_cb(progress->data); - - ecore_event_handler_del(_data); - ecore_event_handler_del(_complete); return EINA_TRUE; } + EAPI void extra_sync(Extra_Progress *progress) { - Ecore_Con_Url *url; + Extra_Sync_Request *req; + + req = calloc(1, sizeof(Extra_Sync_Request)); - url = ecore_con_url_custom_new("http://" HOSTNAME "/v1/themes/", "GET"); - ecore_con_url_additional_header_add(url, "Accept", "text/json"); - ecore_con_url_data_set(url, eina_strbuf_new()); + req->progress = progress; - _data = ecore_event_handler_add(ECORE_CON_EVENT_URL_DATA, _url_data_cb, NULL); - _complete = ecore_event_handler_add(ECORE_CON_EVENT_URL_COMPLETE, _url_complete_cb, progress); + req->themes = ecore_con_url_custom_new("http://" HOSTNAME "/v1/themes/", "GET"); + ecore_con_url_additional_header_add(req->themes, "Accept", "text/json"); + ecore_con_url_data_set(req->themes, eina_strbuf_new()); - ecore_con_url_get(url); + req->backgrounds = ecore_con_url_custom_new("http://" HOSTNAME "/v1/backgrounds/", "GET"); + ecore_con_url_additional_header_add(req->backgrounds, "Accept", "text/json"); + ecore_con_url_data_set(req->backgrounds, eina_strbuf_new()); + + _data = ecore_event_handler_add(ECORE_CON_EVENT_URL_DATA, _url_data_cb, req); + _complete = ecore_event_handler_add(ECORE_CON_EVENT_URL_COMPLETE, _url_complete_cb, req); + + ecore_con_url_get(req->backgrounds); + ecore_con_url_get(req->themes); } EAPI Eina_List * @@ -466,7 +479,6 @@ extra_theme_download_url_get(Extra_Theme *theme) return url; } - EAPI void extra_theme_download(Extra_Progress *progress, Extra_Theme *theme) { @@ -494,7 +506,7 @@ extra_theme_download(Extra_Progress *progress, Extra_Theme *theme) void _extra_theme_cache_load() { - char *cache_path = _theme_cache_path_get(); + char *cache_path = _cache_path_get("themes"); if (ecore_file_exists(cache_path)) { diff --git a/src/lib/extra.h b/src/lib/extra.h index 00c3850..41f9be8 100644 --- a/src/lib/extra.h +++ b/src/lib/extra.h @@ -50,6 +50,14 @@ typedef struct _Extra_Theme int version; } Extra_Theme; +typedef struct _Extra_Background +{ + const char *id; + const char *name; + const char *author; + int version; +} Extra_Background; + typedef struct _Extra_Progress { Extra_Progress_Cb progress_cb; diff --git a/src/lib/extra_api_helper.c b/src/lib/extra_api_helper.c new file mode 100644 index 0000000..4fd8299 --- /dev/null +++ b/src/lib/extra_api_helper.c @@ -0,0 +1,192 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stddef.h> +#include <sys/stat.h> + +#include "extra.h" +#include "jsmn/jsmn.h" +#include "extra_private.h" + + +static Eina_Strbuf* +_get_json_syntax(Eina_Strbuf *buf, jsmntok_t *tok) +{ + return eina_strbuf_substr_get(buf, tok->start, tok->end - tok->start); +} + +static int +_string_tuple_get(Eina_Strbuf *buf, jsmntok_t *array, int i, Eina_Strbuf **name, Eina_Strbuf **value) +{ + if (array[i].type != JSMN_STRING || array[i].size != 1) + { + printf("expected string type with children\n"); + return 0; + } + + *name = _get_json_syntax(buf, &array[i]); + + if ((array[i + 1].type != JSMN_STRING && array[i + 1].type != JSMN_PRIMITIVE) || array[i + 1].size != 0) + { + printf("Expected string type without children\n"); + return 0; + } + + *value = _get_json_syntax(buf, &array[i + 1]); + + return 2; +} + +//get the value of a instance described with the given part +static Eina_Strbuf* +_field_get(void *instance, Extra_Instance_Part *part) +{ + return *((Eina_Strbuf**)(instance + part->size)); +} + +//set the value of a instance described with the given part +static void +_field_set(void *instance, Extra_Instance_Part *part, Eina_Strbuf *data) +{ + *((Eina_Strbuf**)(instance + part->size)) = data; +} + +void +extra_json_list_part_free(Extra_Json_To_List_Template *tmp, void *instance) +{ + for (unsigned int j = 0; j < tmp->tuples_size; ++j) + { + Eina_Strbuf *c; + c = _field_get(instance, &tmp->tuples[j]); + if (c) + eina_strbuf_free(c); + } + free(instance); +} + +static int +_instance_new(Extra_Json_To_List_Template *tmp, Eina_Strbuf *buf, jsmntok_t parts[], void **ret) +{ + void *instance = calloc(1, tmp->structsize); + int c = 0; + + c++; + *ret = NULL; + + for (int i = 0; i < parts[0].size; ++i) + { + Eina_Strbuf *key = NULL, *data = NULL; + Eina_Bool success = EINA_FALSE; + int j; + const char *v; + j = _string_tuple_get(buf, parts, c, &key, &data); + + if (!j) return 0; + c += j; + + v = eina_strbuf_string_get(key); + + //map to the list of write tuples + for (unsigned int j = 0; j < tmp->tuples_size; ++j) + { + if (!strcmp(tmp->tuples[j].name, v)) + { + if (_field_get(instance, &tmp->tuples[j])) + { + Eina_Strbuf *buf = NULL; + buf = _get_json_syntax(buf, &parts[i]); + //double setting the same field + ERR("In instance %p, field %s was set twice!\n%s\n", instance, tmp->tuples[i].name, eina_strbuf_string_get(buf)); + eina_strbuf_free(buf); + + extra_json_list_part_free(tmp, instance); + instance = NULL; + return 0; + } + _field_set(instance, &tmp->tuples[j], data); + success = EINA_TRUE; + break; + } + } + + //silently ignore data key pairs that are not found + //this enables changing the protocol without killing the app + if (!success) + eina_strbuf_free(data); + eina_strbuf_free(key); + } + + //validate that the new instance has all required fields + for (unsigned int i = 0; i < tmp->tuples_size; ++i) + { + if (tmp->tuples[i].required && !_field_get(instance, &tmp->tuples[i])) + { + ERR("In instance %p, required field %s was not set!\n", instance, tmp->tuples[i].name); + //free all set values + extra_json_list_part_free(tmp, instance); + instance = NULL; + break; + } + } + + + *ret = instance; + return c; +} + +Eina_List* +extra_json_to_list(Extra_Json_To_List_Template *tmp, Eina_Strbuf *buf) +{ + jsmn_parser parser; + Eina_List *ret = NULL; + jsmntok_t parts[201]; + const char *string; + int n = 0; + int c = 0; + + jsmn_init(&parser); + + //parse the json text + string = eina_strbuf_string_get(buf); + n = jsmn_parse(&parser, string, strlen(string), parts, sizeof(parts)); + + if (n == 0) return NULL; + + if (parts[0].type != JSMN_OBJECT) + { + printf("Root node should be a object"); + return NULL; + } + c ++; + + for (int i = 0; i < parts[0].size; ++i) + { + void *instance; + int j; + + if (parts[c].type != JSMN_STRING || parts[c].size != 1) + { + printf("Expected String type with one child\n"); + return NULL; + } + c ++; + + if (parts[c].type != JSMN_OBJECT || parts[c].size <= 0) + { + printf("Expected Object type with more than 0 children\n"); + return NULL; + } + + //create a instance for that object + j = _instance_new(tmp, buf, parts + c, &instance); + if (!j) return NULL; + c += j; + + //add this to the list of instances + if (instance) + ret = eina_list_append(ret, instance); + + } + return ret; +} diff --git a/src/lib/extra_private.h b/src/lib/extra_private.h index 2c19198..d097557 100644 --- a/src/lib/extra_private.h +++ b/src/lib/extra_private.h @@ -26,4 +26,29 @@ extern int _extra_lib_log_dom; #endif #define DBG(...) EINA_LOG_DOM_DBG(_extra_lib_log_dom, __VA_ARGS__) +typedef struct { + const char *name; + size_t size; // this pointer constists out of instance + ptr + Eina_Bool required; +} Extra_Instance_Part; + +typedef struct { + size_t structsize; + Extra_Instance_Part *tuples; + size_t tuples_size; +} Extra_Json_To_List_Template; + +#define EXTRA_JSON_STRUCT_FIELD(name, str, val, required) { name , ((size_t)&(((str *)0)->val)), required } + +#define EXTRA_JSON_TO_LIST_TEMPLATE_INIT(v, str, ...) \ +Extra_Instance_Part part[] = {__VA_ARGS__}; \ +Extra_Json_To_List_Template v = { \ + sizeof(str), \ + part, \ + sizeof(part)/sizeof(Extra_Instance_Part) \ +} + +Eina_List* extra_json_to_list(Extra_Json_To_List_Template *tmp, Eina_Strbuf *buf); +void extra_json_list_part_free(Extra_Json_To_List_Template *tmp, void *data); + #endif --