diff --git a/src/Makefile.am b/src/Makefile.am index 1792f9d..ff28d75 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -8,7 +8,9 @@ INCLUDES = -I. \ pkgdir = $(datadir)/$(MODULE_ARCH) pkg_LTLIBRARIES = module.la module_la_SOURCES = e_mod_main.h \ - e_mod_main.c + e_mod_main.c \ + json.h \ + json.c module_la_LIBADD = @EVRY_LIBS@ module_la_LDFLAGS = -module -avoid-version diff --git a/src/e_mod_main.c b/src/e_mod_main.c index 515e07f..e6ce467 100644 --- a/src/e_mod_main.c +++ b/src/e_mod_main.c @@ -4,8 +4,10 @@ #include #include +#include #include "e_mod_main.h" +#include "json.h" #define ACT_GOOGLE 1 #define ACT_FEELING_LUCKY 2 @@ -70,6 +72,118 @@ static char _address_wiki[] = "www.wikipedia.org"; static const char *_id_none; static const char _imgur_key[] = "1606e11f5c2ccd9b7440f1ffd80b17de"; + +typedef struct _Json_Data Json_Data; + +struct _Json_Data +{ + int flat; + + Json_Data *parent; + Json_Data *cur; + + int type; + + const char *key; + const char *value; + + Eina_List *list; +}; + +static +int _parse_callback(void *userdata, int type, const char *data, uint32_t length) +{ + Json_Data *d = userdata; + Json_Data *d2; + + switch (type) + { + case JSON_OBJECT_BEGIN: + case JSON_ARRAY_BEGIN: + if (d->cur) + d->cur->type = type; + if (d->flat) + d->cur = d->parent; + break; + + case JSON_OBJECT_END: + case JSON_ARRAY_END: + d->cur = d->parent; + break; + + case JSON_KEY: + d2 = E_NEW(Json_Data, 1); + if (d2->key) eina_stringshare_del(d->key); + d2->key = eina_stringshare_add_length(data, length); + d->cur = d2; + d2->parent = d; + d->list = eina_list_append(d->list, d2); + break; + + case JSON_STRING: + case JSON_INT: + case JSON_FLOAT: + d->cur->type = type; + if (d->cur->value) eina_stringshare_del(d->cur->value); + d->cur->value = eina_stringshare_add_length(data, length); + d->cur = d->parent; + break; + + case JSON_NULL: + case JSON_TRUE: + case JSON_FALSE: + d->cur = d->parent; + d->cur = d->parent; + d->cur = d->parent; + } + + return 0; +} + +static Json_Data * +_parse_json(char *string, Eina_Bool flat, int len) +{ + Eina_Hash *h; + struct json_parser parser; + int i, ret; + + if (!len) + len = strlen(string); + + if (!string) + return NULL; + + Json_Data *d = E_NEW(Json_Data, 1); + d->flat = flat; + + if (json_parser_init(&parser, NULL, _parse_callback, d)) + { + fprintf(stderr, "something wrong happened during init\n"); + E_FREE(d); + return NULL; + } + + for (i = 0; i < len; i += 1) + { + ret = json_parser_string(&parser, string + i, 1, NULL); + if (ret) + { + printf("error %d\n", ret); + break; + } + } + + json_parser_free(&parser); + + if (ret) + { + E_FREE(d); + return NULL; + } + + return d; +} + int _server_data(void *data, int ev_type, Ecore_Con_Event_Server_Data *ev) { @@ -211,7 +325,7 @@ _fetch(Evry_Plugin *plugin, const char *input) { EVRY_PLUGIN_ITEMS_FREE(p); } - + return 0; } @@ -270,107 +384,178 @@ _action(Evry_Action *act) E_FREE(app); } -/* static Ecore_Event_Handler *con_complete; - * static Ecore_Event_Handler *con_data; - * static Ecore_Event_Handler *con_progress; - * - * static int - * _con_complete(void *data, int ev_type, void *event) - * { - * Ecore_Con_Event_Url_Complete *ev = event; - * const Eina_List *l, *ll; - * - * if (data != _act4) - * return; - * - * printf("completed\n"); - * - * char *reply = ecore_con_url_data_get(ev->url_con); - * char *header; - * - * l = ecore_con_url_response_headers_get(ev->url_con); - * - * EINA_LIST_FOREACH(l, ll, header) - * printf("%s", header); - * - * ecore_event_handler_del(con_complete); - * ecore_event_handler_del(con_data); - * - * ecore_con_url_destroy(ev->url_con); - * - * return 0; - * } - * - * static int - * _con_data(void *data, int ev_type, void *event) - * { - * Ecore_Con_Event_Url_Data *ev = event; - * - * if (data != _act4) - * return; - * - * printf("reply %s %d\n", - * ev->data, - * ecore_con_url_received_bytes_get(ev->url_con)); - * - * return 0; - * } */ +static Ecore_Event_Handler *con_complete; +static Ecore_Event_Handler *con_data; +static Ecore_Event_Handler *con_progress; -/* static int - * _con_url_progress(void *data, int ev_type, void *event) - * { - * Ecore_Con_Event_Url_Progress *ev = event; - * - * return 1; - * } */ +static int +_con_complete(void *data, int ev_type, void *event) +{ + Ecore_Con_Event_Url_Complete *ev = event; + const Eina_List *l, *ll; -/* static int - * _action_upload_check(Evry_Action *act, const Evry_Item *item) - * { - * GET_FILE(file, item); - * - * if (!file->mime || strncmp(file->mime, "image/", 6)) - * return 0; - * - * return 1; - * } - * - * static int - * _action_upload(Evry_Action *act) - * { - * struct stat info; - * struct curl_httppost* post = NULL; - * struct curl_httppost* last = NULL; - * int ret; - * - * GET_FILE(file, act->it1.item); - * if (!evry_file_path_get(file)) - * return 0; - * - * Ecore_Con_Url *con_url = ecore_con_url_new("http://imgur.com/api/upload.json"); - * con_complete = ecore_event_handler_add - * (ECORE_CON_EVENT_URL_COMPLETE, _con_complete, act); - * - * con_data = ecore_event_handler_add - * (ECORE_CON_EVENT_URL_DATA, _con_data, act); - * - * /\* con_url_progress = ecore_event_handler_add - * * (ECORE_CON_EVENT_URL_PROGRESS, _con_url_progress, act); *\/ - * - * ret = curl_formadd(&post, &last, - * CURLFORM_COPYNAME, "key", - * CURLFORM_COPYCONTENTS, _imgur_key, - * CURLFORM_END); - * - * ret = curl_formadd(&post, &last, - * CURLFORM_COPYNAME, "image", - * CURLFORM_FILE, file->path, - * CURLFORM_END); - * - * ecore_con_url_http_post_send(con_url, post); - * - * return 0; - * } */ + if (data != _act4) + return; + + printf("completed\n"); + + char *reply = ecore_con_url_data_get(ev->url_con); + char *header; + + l = ecore_con_url_response_headers_get(ev->url_con); + + EINA_LIST_FOREACH(l, ll, header) + printf("%s", header); + + ecore_event_handler_del(con_complete); + ecore_event_handler_del(con_data); + + ecore_con_url_destroy(ev->url_con); + + return 0; +} + +static Json_Data * +_json_data_find(const Json_Data *jd, const char *key) +{ + Json_Data *d = NULL; + Eina_List *l; + const char *k; + + if (!jd) return NULL; + + k = eina_stringshare_add(key); + + EINA_LIST_FOREACH(jd->list, l, d) + if (d->key == k) + break; + + eina_stringshare_del(k); + + return d; +} + +static void +_json_data_free(Json_Data *jd) +{ + Json_Data *d; + + if (!d) return; + + EINA_LIST_FREE(jd->list, d) + { + printf("'%s':'%s'\n", d->key, d->value); + + if (d->key) eina_stringshare_del(d->key); + if (d->value) eina_stringshare_del(d->value); + E_FREE(d); + } + E_FREE(jd); +} + + +static int +_con_data(void *data, int ev_type, void *event) +{ + Ecore_Con_Event_Url_Data *ev = event; + Json_Data *d, *rsp; + Evry_Item_File *file; + int len, cb = 0; + + if (data != _act4) + return; + + len = ecore_con_url_received_bytes_get(ev->url_con); + file = ecore_con_url_data_get(ev->url_con); + + rsp = _parse_json((char *)ev->data, 1, len); + + d = _json_data_find(rsp, "imgur_page"); + if (d) + { + len = strlen(d->value); + ecore_x_selection_primary_set(ecore_x_window_root_first_get(), d->value, len); + ecore_x_selection_clipboard_set(ecore_x_window_root_first_get(), d->value, len); + cb = 1; + } + _json_data_free(rsp); + + E_Notification *n; + if (cb) + n = e_notification_full_new("Everything", 0, "image", "Image Sent", "Link copied to clipboard", -1); + else + n = e_notification_full_new("Everything", 0, "image", "Image Sent", file->path, -1); + + e_notification_send(n, NULL, NULL); + e_notification_unref(n); + + EVRY_ITEM_FREE(file); + + return 0; +} + +static int +_con_url_progress(void *data, int ev_type, void *event) +{ + Ecore_Con_Event_Url_Progress *ev = event; + + return 1; +} + +static int +_action_upload_check(Evry_Action *act, const Evry_Item *item) +{ + GET_FILE(file, item); + + if (!file->mime || strncmp(file->mime, "image/", 6)) + return 0; + + return 1; +} + +static int +_action_upload(Evry_Action *act) +{ + struct stat info; + struct curl_httppost* post = NULL; + struct curl_httppost* last = NULL; + E_Notification *n; + int ret; + + GET_FILE(file, act->it1.item); + if (!evry_file_path_get(file)) + return 0; + + Ecore_Con_Url *con_url = ecore_con_url_new("http://imgur.com/api/upload.json"); + con_complete = ecore_event_handler_add + (ECORE_CON_EVENT_URL_COMPLETE, _con_complete, act); + + con_data = ecore_event_handler_add + (ECORE_CON_EVENT_URL_DATA, _con_data, act); + + evry_item_ref(EVRY_ITEM(file)); + ecore_con_url_data_set(con_url, file); + + /* con_url_progress = ecore_event_handler_add + * (ECORE_CON_EVENT_URL_PROGRESS, _con_url_progress, act); */ + + ret = curl_formadd(&post, &last, + CURLFORM_COPYNAME, "key", + CURLFORM_COPYCONTENTS, _imgur_key, + CURLFORM_END); + + ret = curl_formadd(&post, &last, + CURLFORM_COPYNAME, "image", + CURLFORM_FILE, file->path, + CURLFORM_END); + + ecore_con_url_http_post_send(con_url, post); + + n = e_notification_full_new("Everything", 0, "image", "Send Image", file->path, -1); + e_notification_send(n, NULL, NULL); + e_notification_unref(n); + return 0; +} Evas_Object * _icon_get(Evry_Item *it, Evas *e) @@ -467,11 +652,11 @@ _plugins_init(void) EVRY_ITEM(_act3)->icon_get = &_icon_get; evry_action_register(_act3, 1); - /* _act4 = EVRY_ACTION_NEW(N_("Upload Image"), EVRY_TYPE_FILE, 0, "go-next", - * _action_upload, _action_upload_check); - * _act4->remember_context = EINA_TRUE; - * EVRY_ITEM_DATA_INT_SET(_act4, ACT_UPLOAD_IMGUR); - * evry_action_register(_act4, 1); */ + _act4 = EVRY_ACTION_NEW(N_("Upload Image"), EVRY_TYPE_FILE, 0, "go-next", + _action_upload, _action_upload_check); + _act4->remember_context = EINA_TRUE; + EVRY_ITEM_DATA_INT_SET(_act4, ACT_UPLOAD_IMGUR); + evry_action_register(_act4, 1); return EINA_TRUE; } @@ -485,7 +670,7 @@ _plugins_shutdown(void) evry_action_free(_act1); evry_action_free(_act2); evry_action_free(_act3); - /* evry_action_free(_act4); */ + evry_action_free(_act4); } /***************************************************************************/ @@ -697,6 +882,8 @@ e_modapi_init(E_Module *m) bindtextdomain(PACKAGE, buf); bind_textdomain_codeset(PACKAGE, "UTF-8"); + e_notification_init(); + if (!ecore_con_init()) return NULL; @@ -728,6 +915,8 @@ e_modapi_shutdown(E_Module *m) ecore_con_shutdown(); + e_notification_shutdown(); + eina_stringshare_del(_id_none); ecore_con_url_shutdown(); diff --git a/src/json.c b/src/json.c new file mode 100644 index 0000000..6c6b50a --- /dev/null +++ b/src/json.c @@ -0,0 +1,963 @@ +/* + * Copyright (C) 2009 Vincent Hanquez + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; version 2.1 or version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * the class, states and state transition tables has been inspired by the JSON_parser.c + * available at http://json.org, but are quite different on the way that the + * parser handles its parse buffer and contains significant differences that affect + * the JSON compliance. + */ + +#include +#include +#include +#include "json.h" + +#ifdef TRACING_ENABLE +#include +#define TRACING(fmt, ...) fprintf(stderr, "tracing: " fmt, ##__VA_ARGS__) +#else +#define TRACING(fmt, ...) ((void) 0) +#endif + +enum classes { + C_SPACE, /* space */ + C_NL, /* newline */ + C_WHITE, /* tab, CR */ + C_LCURB, C_RCURB, /* object opening/closing */ + C_LSQRB, C_RSQRB, /* array opening/closing */ + /* syntax symbols */ + C_COLON, + C_COMMA, + C_QUOTE, /* " */ + C_BACKS, /* \ */ + C_SLASH, /* / */ + C_PLUS, + C_MINUS, + C_DOT, + C_ZERO, C_DIGIT, /* digits */ + C_a, C_b, C_c, C_d, C_e, C_f, C_l, C_n, C_r, C_s, C_t, C_u, /* nocaps letters */ + C_ABCDF, C_E, /* caps letters */ + C_OTHER, /* all other */ + C_STAR, /* star in C style comment */ + C_HASH, /* # for YAML comment */ + C_ERROR = 0xfe, +}; + +/* map from character < 128 to classes. from 128 to 256 all C_OTHER */ +static uint8_t character_class[128] = { + C_ERROR, C_ERROR, C_ERROR, C_ERROR, C_ERROR, C_ERROR, C_ERROR, C_ERROR, + C_ERROR, C_WHITE, C_NL, C_ERROR, C_ERROR, C_WHITE, C_ERROR, C_ERROR, + C_ERROR, C_ERROR, C_ERROR, C_ERROR, C_ERROR, C_ERROR, C_ERROR, C_ERROR, + C_ERROR, C_ERROR, C_ERROR, C_ERROR, C_ERROR, C_ERROR, C_ERROR, C_ERROR, + + C_SPACE, C_OTHER, C_QUOTE, C_HASH, C_OTHER, C_OTHER, C_OTHER, C_OTHER, + C_OTHER, C_OTHER, C_STAR, C_PLUS, C_COMMA, C_MINUS, C_DOT, C_SLASH, + C_ZERO, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, + C_DIGIT, C_DIGIT, C_COLON, C_OTHER, C_OTHER, C_OTHER, C_OTHER, C_OTHER, + + C_OTHER, C_ABCDF, C_ABCDF, C_ABCDF, C_ABCDF, C_E, C_ABCDF, C_OTHER, + C_OTHER, C_OTHER, C_OTHER, C_OTHER, C_OTHER, C_OTHER, C_OTHER, C_OTHER, + C_OTHER, C_OTHER, C_OTHER, C_OTHER, C_OTHER, C_OTHER, C_OTHER, C_OTHER, + C_OTHER, C_OTHER, C_OTHER, C_LSQRB, C_BACKS, C_RSQRB, C_OTHER, C_OTHER, + + C_OTHER, C_a, C_b, C_c, C_d, C_e, C_f, C_OTHER, + C_OTHER, C_OTHER, C_OTHER, C_OTHER, C_l, C_OTHER, C_n, C_OTHER, + C_OTHER, C_OTHER, C_r, C_s, C_t, C_u, C_OTHER, C_OTHER, + C_OTHER, C_OTHER, C_OTHER, C_LCURB, C_OTHER, C_RCURB, C_OTHER, C_OTHER +}; + +/* only the first 36 ascii characters need an escape */ +static char *character_escape[36] = { + "\\u0000", "\\u0001", "\\u0002", "\\u0003", "\\u0004", "\\u0005", "\\u0006", "\\u0007", /* 0-7 */ + "\\b" , "\\t", "\\n", "\\u000b", "\\f", "\\r", "\\u000e", "\\u000f", /* 8-f */ + "\\u0010", "\\u0011", "\\u0012", "\\u0013", "\\u0014", "\\u0015", "\\u0016", "\\u0017", /* 10-17 */ + "\\u0018", "\\u0019", "\\u001a", "\\u001b", "\\u001c", "\\u001d", "\\u001e", "\\u001f", /* 18-1f */ + " " , "!" , "\\\"" , "#", +}; + +/* define all states and actions that will be taken on each transition. + * + * states are defined first because of the fact they are use as index in the + * transitions table. they usually contains either a number or a prefix _ + * for simple state like string, object, value ... + * + * actions are defined starting from 0x80. state error is defined as 0xff + */ + +enum states { + STATE_GO, /* start */ + STATE_OK, /* ok */ + STATE__O, /* object */ + STATE__K, /* key */ + STATE_CO, /* colon */ + STATE__V, /* value */ + STATE__A, /* array */ + STATE__S, /* string */ + STATE_E0, /* escape */ + STATE_U1, STATE_U2, STATE_U3, STATE_U4, /* unicode states */ + STATE_M0, STATE_Z0, STATE_I0, /* number states */ + STATE_R1, STATE_R2, /* real states (after-dot digits) */ + STATE_X1, STATE_X2, STATE_X3, /* exponant states */ + STATE_T1, STATE_T2, STATE_T3, /* true constant states */ + STATE_F1, STATE_F2, STATE_F3, STATE_F4, /* false constant states */ + STATE_N1, STATE_N2, STATE_N3, /* null constant states */ + STATE_C1, STATE_C2, STATE_C3, /* C-comment states */ + STATE_Y1, /* YAML-comment state */ + STATE_D1, STATE_D2, /* multi unicode states */ +}; + +/* the following are actions that need to be taken */ +enum actions { + STATE_KS = 0x80, /* key separator */ + STATE_SP, /* comma separator */ + STATE_AB, /* array begin */ + STATE_AE, /* array ending */ + STATE_OB, /* object begin */ + STATE_OE, /* object end */ + STATE_CB, /* C-comment begin */ + STATE_YB, /* YAML-comment begin */ + STATE_CE, /* YAML/C comment end */ + STATE_FA, /* false */ + STATE_TR, /* true */ + STATE_NU, /* null */ + STATE_DE, /* double detected by exponent */ + STATE_DF, /* double detected by . */ + STATE_SE, /* string end */ + STATE_MX, /* integer detected by minus */ + STATE_ZX, /* integer detected by zero */ + STATE_IX, /* integer detected by 1-9 */ + STATE_UC, /* Unicode character read */ +}; + +/* error state */ +#define STATE___ 0xff + +#define NR_STATES (STATE_D2 + 1) +#define NR_CLASSES (C_HASH + 1) + +#define IS_STATE_ACTION(s) ((s) & 0x80) +#define S(x) STATE_##x +#define PT_(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,a1,b1,c1,d1,e1,f1,g1,h1) \ + { S(a),S(b),S(c),S(d),S(e),S(f),S(g),S(h),S(i),S(j),S(k),S(l),S(m),S(n), \ + S(o),S(p),S(q),S(r),S(s),S(t),S(u),S(v),S(w),S(x),S(y),S(z),S(a1),S(b1), \ + S(c1),S(d1),S(e1),S(f1),S(g1),S(h1) } + +/* map from the (previous state+new character class) to the next parser transition */ +static const uint8_t state_transition_table[NR_STATES][NR_CLASSES] = { +/* white ABCDF other */ +/* sp nl | { } [ ] : , " \ / + - . 0 19 a b c d e f l n r s t u | E | * # */ +/*GO*/ PT_(GO,GO,GO,OB,__,AB,__,__,__,__,__,CB,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,YB), +/*OK*/ PT_(OK,OK,OK,__,OE,__,AE,__,SP,__,__,CB,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,YB), +/*_O*/ PT_(_O,_O,_O,__,OE,__,__,__,__,_S,__,CB,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,YB), +/*_K*/ PT_(_K,_K,_K,__,__,__,__,__,__,_S,__,CB,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,YB), +/*CO*/ PT_(CO,CO,CO,__,__,__,__,KS,__,__,__,CB,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,YB), +/*_V*/ PT_(_V,_V,_V,OB,__,AB,__,__,__,_S,__,CB,__,MX,__,ZX,IX,__,__,__,__,__,F1,__,N1,__,__,T1,__,__,__,__,__,YB), +/*_A*/ PT_(_A,_A,_A,OB,__,AB,AE,__,__,_S,__,CB,__,MX,__,ZX,IX,__,__,__,__,__,F1,__,N1,__,__,T1,__,__,__,__,__,YB), +/****************************************************************************************************************/ +/*_S*/ PT_(_S,__,__,_S,_S,_S,_S,_S,_S,SE,E0,_S,_S,_S,_S,_S,_S,_S,_S,_S,_S,_S,_S,_S,_S,_S,_S,_S,_S,_S,_S,_S,_S,_S), +/*E0*/ PT_(__,__,__,__,__,__,__,__,__,_S,_S,_S,__,__,__,__,__,__,_S,__,__,__,_S,__,_S,_S,__,_S,U1,__,__,__,__,__), +/*U1*/ PT_(__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,U2,U2,U2,U2,U2,U2,U2,U2,__,__,__,__,__,__,U2,U2,__,__,__), +/*U2*/ PT_(__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,U3,U3,U3,U3,U3,U3,U3,U3,__,__,__,__,__,__,U3,U3,__,__,__), +/*U3*/ PT_(__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,U4,U4,U4,U4,U4,U4,U4,U4,__,__,__,__,__,__,U4,U4,__,__,__), +/*U4*/ PT_(__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,UC,UC,UC,UC,UC,UC,UC,UC,__,__,__,__,__,__,UC,UC,__,__,__), +/****************************************************************************************************************/ +/*M0*/ PT_(__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,Z0,I0,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__), +/*Z0*/ PT_(OK,OK,OK,__,OE,__,AE,__,SP,__,__,CB,__,__,DF,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,YB), +/*I0*/ PT_(OK,OK,OK,__,OE,__,AE,__,SP,__,__,CB,__,__,DF,I0,I0,__,__,__,__,DE,__,__,__,__,__,__,__,__,DE,__,__,YB), +/*R1*/ PT_(__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,R2,R2,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__), +/*R2*/ PT_(OK,OK,OK,__,OE,__,AE,__,SP,__,__,CB,__,__,__,R2,R2,__,__,__,__,X1,__,__,__,__,__,__,__,__,X1,__,__,YB), +/*X1*/ PT_(__,__,__,__,__,__,__,__,__,__,__,__,X2,X2,__,X3,X3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__), +/*X2*/ PT_(__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,X3,X3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__), +/*X3*/ PT_(OK,OK,OK,__,OE,__,AE,__,SP,__,__,__,__,__,__,X3,X3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__), +/****************************************************************************************************************/ +/*T1*/ PT_(__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,T2,__,__,__,__,__,__,__,__), +/*T2*/ PT_(__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,T3,__,__,__,__,__), +/*T3*/ PT_(__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,TR,__,__,__,__,__,__,__,__,__,__,__,__), +/*F1*/ PT_(__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F2,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__), +/*F2*/ PT_(__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F3,__,__,__,__,__,__,__,__,__,__), +/*F3*/ PT_(__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F4,__,__,__,__,__,__,__), +/*F4*/ PT_(__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,FA,__,__,__,__,__,__,__,__,__,__,__,__), +/*N1*/ PT_(__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,N2,__,__,__,__,__), +/*N2*/ PT_(__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,N3,__,__,__,__,__,__,__,__,__,__), +/*N3*/ PT_(__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,NU,__,__,__,__,__,__,__,__,__,__), +/****************************************************************************************************************/ +/*C1*/ PT_(__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,C2,__), +/*C2*/ PT_(C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C3,C2), +/*C3*/ PT_(C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,CE,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C3,C2), +/*Y1*/ PT_(Y1,CE,Y1,Y1,Y1,Y1,Y1,Y1,Y1,Y1,Y1,Y1,Y1,Y1,Y1,Y1,Y1,Y1,Y1,Y1,Y1,Y1,Y1,Y1,Y1,Y1,Y1,Y1,Y1,Y1,Y1,Y1,Y1,Y1), +/*D1*/ PT_(__,__,__,__,__,__,__,__,__,__,D2,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__), +/*D2*/ PT_(__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,U1,__,__,__,__,__), +}; +#undef S +#undef PT_ + +/* map from (previous state+new character class) to the buffer policy. ignore=0/append=1/escape=2 */ +static const uint8_t buffer_policy_table[NR_STATES][NR_CLASSES] = { +/* white ABCDF other */ +/* sp nl | { } [ ] : , " \ / + - . 0 19 a b c d e f l n r s t u | E | * # */ +/*GO*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/*OK*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/*_O*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/*_K*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/*CO*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/*_V*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/*_A*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/**************************************************************************************************************/ +/*_S*/ { 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, +/*E0*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2, 0, 2, 2, 0, 2, 0, 0, 0, 0, 0, 0 }, +/*U1*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0 }, +/*U2*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0 }, +/*U3*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0 }, +/*U4*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0 }, +/**************************************************************************************************************/ +/*M0*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/*Z0*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/*I0*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, +/*R1*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/*R2*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, +/*X1*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/*X2*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/*X3*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/**************************************************************************************************************/ +/*T1*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/*T2*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/*T3*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/*F1*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/*F2*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/*F3*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/*F4*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/*N1*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/*N2*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/*N3*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/**************************************************************************************************************/ +/*C1*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/*C2*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/*C3*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/*Y1*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/*D1*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +/*D2*/ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, +}; + +#define MODE_ARRAY 0 +#define MODE_OBJECT 1 + +static inline void *memory_realloc(void *(*realloc_fct)(void *, size_t), void *ptr, size_t size) +{ + return (realloc_fct) ? realloc_fct(ptr, size) : realloc(ptr, size); +} + +static inline void *memory_calloc(void *(*calloc_fct)(size_t, size_t), size_t nmemb, size_t size) +{ + return (calloc_fct) ? calloc_fct(nmemb, size) : calloc(nmemb, size); +} + +#define parser_calloc(parser, n, s) memory_calloc(parser->config.user_calloc, n, s) +#define parser_realloc(parser, n, s) memory_realloc(parser->config.user_realloc, n, s) + +static int state_grow(json_parser *parser) +{ + uint32_t newsize = parser->stack_size * 2; + void *ptr; + + if (parser->config.max_nesting != 0) + return JSON_ERROR_NESTING_LIMIT; + + ptr = parser_realloc(parser, parser->stack, newsize * sizeof(uint8_t)); + if (!ptr) + return JSON_ERROR_NO_MEMORY; + parser->stack = ptr; + parser->stack_size = newsize; + return 0; +} + +static int state_push(json_parser *parser, int mode) +{ + if (parser->stack_offset >= parser->stack_size) { + int ret = state_grow(parser); + if (ret) + return ret; + } + parser->stack[parser->stack_offset++] = mode; + return 0; +} + +static int state_pop(json_parser *parser, int mode) +{ + if (parser->stack_offset == 0) + return JSON_ERROR_POP_EMPTY; + parser->stack_offset--; + if (parser->stack[parser->stack_offset] != mode) + return JSON_ERROR_POP_UNEXPECTED_MODE; + return 0; +} + +static int buffer_grow(json_parser *parser) +{ + uint32_t newsize; + void *ptr; + int max = parser->config.max_data; + + if (max > 0 && parser->buffer_size == max) + return JSON_ERROR_DATA_LIMIT; + newsize = parser->buffer_size * 2; + if (max > 0 && newsize > max) + newsize = max; + + ptr = parser_realloc(parser, parser->buffer, newsize * sizeof(char)); + if (!ptr) + return JSON_ERROR_NO_MEMORY; + parser->buffer = ptr; + parser->buffer_size = newsize; + return 0; +} + +static int buffer_push(json_parser *parser, unsigned char c) +{ + int ret; + + if (parser->buffer_offset + 1 >= parser->buffer_size) { + ret = buffer_grow(parser); + if (ret) + return ret; + } + parser->buffer[parser->buffer_offset++] = c; + return 0; +} + +static int do_callback_withbuf(json_parser *parser, int type) +{ + if (!parser->callback) + return 0; + parser->buffer[parser->buffer_offset] = '\0'; + return (*parser->callback)(parser->userdata, type, parser->buffer, parser->buffer_offset); +} + +static int do_callback(json_parser *parser, int type) +{ + if (!parser->callback) + return 0; + return (*parser->callback)(parser->userdata, type, NULL, 0); +} + +static int do_buffer(json_parser *parser) +{ + int ret = 0; + + switch (parser->type) { + case JSON_KEY: case JSON_STRING: + case JSON_FLOAT: case JSON_INT: + case JSON_NULL: case JSON_TRUE: case JSON_FALSE: + ret = do_callback_withbuf(parser, parser->type); + if (ret) + return ret; + break; + default: + break; + } + parser->buffer_offset = 0; + return ret; +} + +static const uint8_t hextable[] = { + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1, + -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1, + -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, +}; + +#define hex(c) (hextable[(uint8_t) c]) + +/* high surrogate range from d800 to dbff */ +/* low surrogate range dc00 to dfff */ +#define IS_HIGH_SURROGATE(uc) (((uc) & 0xfc00) == 0xd800) +#define IS_LOW_SURROGATE(uc) (((uc) & 0xfc00) == 0xdc00) + +/* transform an unicode [0-9A-Fa-f]{4} sequence into a proper value */ +static int decode_unicode_char(json_parser *parser) +{ + uint32_t uval; + char *b = parser->buffer; + int offset = parser->buffer_offset; + + uval = (hex(b[offset - 4]) << 12) | (hex(b[offset - 3]) << 8) + | (hex(b[offset - 2]) << 4) | hex(b[offset - 1]); + + parser->buffer_offset -= 4; + + /* fast case */ + if (!parser->unicode_multi && uval < 0x80) { + b[parser->buffer_offset++] = (char) uval; + return 0; + } + + if (parser->unicode_multi) { + if (!IS_LOW_SURROGATE(uval)) + return JSON_ERROR_UNICODE_MISSING_LOW_SURROGATE; + + uval = 0x10000 + ((parser->unicode_multi & 0x3ff) << 10) + (uval & 0x3ff); + b[parser->buffer_offset++] = (char) ((uval >> 18) | 0xf0); + b[parser->buffer_offset++] = (char) (((uval >> 12) & 0x3f) | 0x80); + b[parser->buffer_offset++] = (char) (((uval >> 6) & 0x3f) | 0x80); + b[parser->buffer_offset++] = (char) ((uval & 0x3f) | 0x80); + parser->unicode_multi = 0; + return 0; + } + + if (IS_LOW_SURROGATE(uval)) + return JSON_ERROR_UNICODE_UNEXPECTED_LOW_SURROGATE; + if (IS_HIGH_SURROGATE(uval)) { + parser->unicode_multi = uval; + return 0; + } + + if (uval < 0x800) { + b[parser->buffer_offset++] = (char) ((uval >> 6) | 0xc0); + b[parser->buffer_offset++] = (char) ((uval & 0x3f) | 0x80); + } else { + b[parser->buffer_offset++] = (char) ((uval >> 12) | 0xe0); + b[parser->buffer_offset++] = (char) (((uval >> 6) & 0x3f) | 0x80); + b[parser->buffer_offset++] = (char) (((uval >> 0) & 0x3f) | 0x80); + } + return 0; +} + +static int buffer_push_escape(json_parser *parser, unsigned char next) +{ + char c = '\0'; + + switch (next) { + case 'b': c = '\b'; break; + case 'f': c = '\f'; break; + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + case '"': c = '"'; break; + case '/': c = '/'; break; + case '\\': c = '\\'; break; + } + /* push the escaped character */ + return buffer_push(parser, c); +} + +#define CHK(f) ({ ret = f; if (ret) return ret; }) + +int act_uc(json_parser *parser) +{ + int ret; + CHK(decode_unicode_char(parser)); + parser->state = (parser->unicode_multi) ? STATE_D1 : STATE__S; + return 0; +} + +int act_yb(json_parser *parser) +{ + if (!parser->config.allow_yaml_comments) + return JSON_ERROR_COMMENT_NOT_ALLOWED; + parser->save_state = parser->state; + return 0; +} + +int act_cb(json_parser *parser) +{ + if (!parser->config.allow_c_comments) + return JSON_ERROR_COMMENT_NOT_ALLOWED; + parser->save_state = parser->state; + return 0; +} + +int act_ce(json_parser *parser) +{ + parser->state = (parser->save_state > STATE__A) ? STATE_OK : parser->save_state; + return 0; +} + +int act_ob(json_parser *parser) +{ + int ret; + CHK(do_callback(parser, JSON_OBJECT_BEGIN)); + CHK(state_push(parser, MODE_OBJECT)); + parser->expecting_key = 1; + return 0; +} + +int act_oe(json_parser *parser) +{ + int ret; + CHK(do_callback(parser, JSON_OBJECT_END)); + CHK(state_pop(parser, MODE_OBJECT)); + parser->expecting_key = 0; + return 0; +} + +int act_ab(json_parser *parser) +{ + int ret; + CHK(do_callback(parser, JSON_ARRAY_BEGIN)); + CHK(state_push(parser, MODE_ARRAY)); + return 0; +} +int act_ae(json_parser *parser) +{ + int ret; + CHK(do_callback(parser, JSON_ARRAY_END)); + CHK(state_pop(parser, MODE_ARRAY)); + return 0; +} + +int act_se(json_parser *parser) +{ + int ret; + CHK(do_callback_withbuf(parser, (parser->expecting_key) ? JSON_KEY : JSON_STRING)); + parser->buffer_offset = 0; + parser->state = (parser->expecting_key) ? STATE_CO : STATE_OK; + parser->expecting_key = 0; + return 0; +} + +int act_sp(json_parser *parser) +{ + if (parser->stack_offset == 0) + return JSON_ERROR_COMMA_OUT_OF_STRUCTURE; + if (parser->stack[parser->stack_offset - 1] == MODE_OBJECT) { + parser->expecting_key = 1; + parser->state = STATE__K; + } else + parser->state = STATE__V; + return 0; +} + +struct action_descr +{ + int (*call)(json_parser *parser); + uint8_t type; + uint8_t state; /* 0 if we let the callback set the value it want */ + uint8_t dobuffer; +}; + +static struct action_descr actions_map[] = { + [STATE_MX & ~0x80] = { NULL, JSON_INT, STATE_M0, 0 }, + [STATE_ZX & ~0x80] = { NULL, JSON_INT, STATE_Z0, 0 }, + [STATE_IX & ~0x80] = { NULL, JSON_INT, STATE_I0, 0 }, + [STATE_DE & ~0x80] = { NULL, JSON_FLOAT, STATE_X1, 0 }, + [STATE_DF & ~0x80] = { NULL, JSON_FLOAT, STATE_R1, 0 }, + [STATE_NU & ~0x80] = { NULL, JSON_NULL, STATE_OK, 0 }, + [STATE_FA & ~0x80] = { NULL, JSON_FALSE, STATE_OK, 0 }, + [STATE_TR & ~0x80] = { NULL, JSON_TRUE, STATE_OK, 0 }, + [STATE_KS & ~0x80] = { NULL, JSON_NONE, STATE__V, 0 }, + [STATE_UC & ~0x80] = { act_uc, JSON_NONE, 0, 0 }, + [STATE_YB & ~0x80] = { act_yb, JSON_NONE, STATE_Y1, 1 }, + [STATE_CB & ~0x80] = { act_cb, JSON_NONE, STATE_C1, 1 }, + [STATE_CE & ~0x80] = { act_ce, JSON_NONE, 0, 0 }, + [STATE_OB & ~0x80] = { act_ob, JSON_NONE, STATE__O, 0 }, + [STATE_OE & ~0x80] = { act_oe, JSON_NONE, STATE_OK, 1 }, + [STATE_AB & ~0x80] = { act_ab, JSON_NONE, STATE__A, 0 }, + [STATE_AE & ~0x80] = { act_ae, JSON_NONE, STATE_OK, 1 }, + [STATE_SE & ~0x80] = { act_se, JSON_NONE, 0, 0 }, + [STATE_SP & ~0x80] = { act_sp, JSON_NONE, 0, 1 }, +}; + +static int do_action(json_parser *parser, int next_state) +{ + struct action_descr *descr = &actions_map[next_state & ~0x80]; + int ret; + + if (descr->call) { + if (descr->dobuffer) + CHK(do_buffer(parser)); + CHK((descr->call)(parser)); + } + if (descr->state) + parser->state = descr->state; + parser->type = descr->type; + return ret; +} + +/** json_parser_init initialize a parser structure taking a config, + * a config and its userdata. + * return JSON_ERROR_NO_MEMORY if memory allocation failed or SUCCESS. + */ +int json_parser_init(json_parser *parser, json_config *config, + json_parser_callback callback, void *userdata) +{ + memset(parser, 0, sizeof(*parser)); + + if (config) + memcpy(&parser->config, config, sizeof(json_config)); + parser->callback = callback; + parser->userdata = userdata; + + /* initialise parsing stack and state */ + parser->stack_offset = 0; + parser->state = STATE_GO; + + /* initialize the parse stack */ + parser->stack_size = (parser->config.max_nesting > 0) + ? parser->config.max_nesting + : LIBJSON_DEFAULT_STACK_SIZE; + + parser->stack = parser_calloc(parser, parser->stack_size, sizeof(parser->stack[0])); + if (!parser->stack) + return JSON_ERROR_NO_MEMORY; + + /* initialize the parse buffer */ + parser->buffer_size = (parser->config.buffer_initial_size > 0) + ? parser->config.buffer_initial_size + : LIBJSON_DEFAULT_BUFFER_SIZE; + + if (parser->config.max_data > 0 && parser->buffer_size > parser->config.max_data) + parser->buffer_size = parser->config.max_data; + + parser->buffer = parser_calloc(parser, parser->buffer_size, sizeof(char)); + if (!parser->buffer) { + free(parser->stack); + return JSON_ERROR_NO_MEMORY; + } + return 0; +} + +/** json_parser_free freed memory structure allocated by the parser */ +int json_parser_free(json_parser *parser) +{ + if (!parser) + return 0; + free(parser->stack); + free(parser->buffer); + parser->stack = NULL; + parser->buffer = NULL; + return 0; +} + +/** json_parser_is_done return 0 is the parser isn't in a finish state. !0 if it is */ +int json_parser_is_done(json_parser *parser) +{ + /* need to compare the state to !GO to not accept empty document */ + return parser->stack_offset == 0 && parser->state != STATE_GO; +} + +/** json_parser_string append a string s with a specific length to the parser + * return 0 if everything went ok, a JSON_ERROR_* otherwise. + * the user can supplied a valid processed pointer that will + * be fill with the number of processed characters before returning */ +int json_parser_string(json_parser *parser, const char *s, + uint32_t length, uint32_t *processed) +{ + int ret; + int next_class, next_state; + int buffer_policy; + uint32_t i; + + ret = 0; + for (i = 0; i < length; i++) { + unsigned char ch = s[i]; + + ret = 0; + next_class = (ch >= 128) ? C_OTHER : character_class[ch]; + if (next_class == C_ERROR) { + ret = JSON_ERROR_BAD_CHAR; + break; + } + + next_state = state_transition_table[parser->state][next_class]; + buffer_policy = buffer_policy_table[parser->state][next_class]; + TRACING("addchar %d (current-state=%d, next-state=%d, buf-policy=%d)\n", + ch, parser->state, next_state, buffer_policy); + if (next_state == STATE___) { + ret = JSON_ERROR_UNEXPECTED_CHAR; + break; + } + + /* add char to buffer */ + if (buffer_policy) { + ret = (buffer_policy == 2) + ? buffer_push_escape(parser, ch) + : buffer_push(parser, ch); + if (ret) + break; + } + + /* move to the next level */ + if (IS_STATE_ACTION(next_state)) + ret = do_action(parser, next_state); + else + parser->state = next_state; + if (ret) + break; + } + if (processed) + *processed = i; + return ret; +} + +/** json_parser_char append one single char to the parser + * return 0 if everything went ok, a JSON_ERROR_* otherwise */ +int json_parser_char(json_parser *parser, unsigned char ch) +{ + return json_parser_string(parser, (char *) &ch, 1, NULL); +} + +/** json_print_init initialize a printer context. always succeed */ +int json_print_init(json_printer *printer, json_printer_callback callback, void *userdata) +{ + memset(printer, '\0', sizeof(*printer)); + printer->callback = callback; + printer->userdata = userdata; + + printer->indentstr = "\t"; + printer->indentlevel = 0; + printer->enter_object = 1; + printer->first = 1; + return 0; +} + +/** json_print_free free a printer a context + * doesn't do anything now, but in future print_init could allocate memory */ +int json_print_free(json_printer *printer) +{ + return 0; +} + +/* escape a C string to be a JSON valid string on the wire. + * XXX: it doesn't do unicode verification. yet?. */ +static int print_string(json_printer *printer, const char *data, uint32_t length) +{ + int i; + + printer->callback(printer->userdata, "\"", 1); + for (i = 0; i < length; i++) { + unsigned char c = data[i]; + if (c < 36) { + char *esc = character_escape[c]; + printer->callback(printer->userdata, esc, strlen(esc)); + } else if (c == '\\') { + printer->callback(printer->userdata, "\\\\", 2); + } else + printer->callback(printer->userdata, data + i, 1); + } + printer->callback(printer->userdata, "\"", 1); + return 0; +} + +static int print_indent(json_printer *printer) +{ + int i; + printer->callback(printer->userdata, "\n", 1); + for (i = 0; i < printer->indentlevel; i++) + printer->callback(printer->userdata, printer->indentstr, strlen(printer->indentstr)); + return 0; +} + +int json_print_mode(json_printer *printer, int type, const char *data, uint32_t length, int pretty) +{ + int enterobj = printer->enter_object; + + if (!enterobj && !printer->afterkey && (type != JSON_ARRAY_END && type != JSON_OBJECT_END)) { + printer->callback(printer->userdata, ",", 1); + if (pretty) print_indent(printer); + } + + if (pretty && (enterobj && !printer->first && (type != JSON_ARRAY_END && type != JSON_OBJECT_END))) { + print_indent(printer); + } + + printer->first = 0; + printer->enter_object = 0; + printer->afterkey = 0; + switch (type) { + case JSON_ARRAY_BEGIN: + printer->callback(printer->userdata, "[", 1); + printer->indentlevel++; + printer->enter_object = 1; + break; + case JSON_OBJECT_BEGIN: + printer->callback(printer->userdata, "{", 1); + printer->indentlevel++; + printer->enter_object = 1; + break; + case JSON_ARRAY_END: + case JSON_OBJECT_END: + printer->indentlevel--; + if (pretty && !enterobj) print_indent(printer); + printer->callback(printer->userdata, (type == JSON_OBJECT_END) ? "}" : "]", 1); + break; + case JSON_INT: printer->callback(printer->userdata, data, length); break; + case JSON_FLOAT: printer->callback(printer->userdata, data, length); break; + case JSON_NULL: printer->callback(printer->userdata, "null", 4); break; + case JSON_TRUE: printer->callback(printer->userdata, "true", 4); break; + case JSON_FALSE: printer->callback(printer->userdata, "false", 5); break; + case JSON_KEY: + print_string(printer, data, length); + printer->callback(printer->userdata, ": ", (pretty) ? 2 : 1); + printer->afterkey = 1; + break; + case JSON_STRING: + print_string(printer, data, length); + break; + default: + break; + } + + return 0; +} + +/** json_print_pretty pretty print the passed argument (type/data/length). */ +int json_print_pretty(json_printer *printer, int type, const char *data, uint32_t length) +{ + return json_print_mode(printer, type, data, length, 1); +} + +/** json_print_raw prints without eye candy the passed argument (type/data/length). */ +int json_print_raw(json_printer *printer, int type, const char *data, uint32_t length) +{ + return json_print_mode(printer, type, data, length, 0); +} + +/** json_print_args takes multiple types and pass them to the printer function */ +int json_print_args(json_printer *printer, + int (*f)(json_printer *, int, const char *, uint32_t), + ...) +{ + va_list ap; + char *data; + uint32_t length; + int type, ret; + + ret = 0; + va_start(ap, f); + while ((type = va_arg(ap, int)) != -1) { + switch (type) { + case JSON_ARRAY_BEGIN: + case JSON_ARRAY_END: + case JSON_OBJECT_BEGIN: + case JSON_OBJECT_END: + case JSON_NULL: + case JSON_TRUE: + case JSON_FALSE: + ret = (*f)(printer, type, NULL, 0); + break; + case JSON_INT: + case JSON_FLOAT: + case JSON_KEY: + case JSON_STRING: + data = va_arg(ap, char *); + length = va_arg(ap, uint32_t); + if (length == -1) + length = strlen(data); + ret = (*f)(printer, type, data, length); + break; + } + if (ret) + break; + } + va_end(ap); + return ret; +} + +static int dom_push(struct json_parser_dom *ctx, void *val) +{ + if (ctx->stack_offset == ctx->stack_size) { + void *ptr; + uint32_t newsize = ctx->stack_size * 2; + ptr = memory_realloc(ctx->user_realloc, ctx->stack, newsize); + if (!ptr) + return JSON_ERROR_NO_MEMORY; + ctx->stack = ptr; + ctx->stack_size = newsize; + } + ctx->stack[ctx->stack_offset].val = val; + ctx->stack[ctx->stack_offset].key = NULL; + ctx->stack[ctx->stack_offset].key_length = 0; + ctx->stack_offset++; + return 0; +} + +static int dom_pop(struct json_parser_dom *ctx, void **val) +{ + ctx->stack_offset--; + *val = ctx->stack[ctx->stack_offset].val; + return 0; +} + +int json_parser_dom_init(json_parser_dom *dom, + json_parser_dom_create_structure create_structure, + json_parser_dom_create_data create_data, + json_parser_dom_append append) +{ + memset(dom, 0, sizeof(*dom)); + dom->stack_size = 1024; + dom->stack_offset = 0; + dom->stack = memory_calloc(dom->user_calloc, dom->stack_size, sizeof(*(dom->stack))); + if (!dom->stack) + return JSON_ERROR_NO_MEMORY; + dom->append = append; + dom->create_structure = create_structure; + dom->create_data = create_data; + return 0; +} + +int json_parser_dom_free(json_parser_dom *dom) +{ + free(dom->stack); + return 0; +} + +int json_parser_dom_callback(void *userdata, int type, const char *data, uint32_t length) +{ + struct json_parser_dom *ctx = userdata; + void *v; + struct stack_elem *stack = NULL; + + switch (type) { + case JSON_ARRAY_BEGIN: + case JSON_OBJECT_BEGIN: + v = ctx->create_structure(ctx->stack_offset, type == JSON_OBJECT_BEGIN); + if (!v) + return JSON_ERROR_CALLBACK; + dom_push(ctx, v); + break; + case JSON_OBJECT_END: + case JSON_ARRAY_END: + dom_pop(ctx, &v); + if (ctx->stack_offset > 0) { + stack = &(ctx->stack[ctx->stack_offset - 1]); + ctx->append(stack->val, stack->key, stack->key_length, v); + free(stack->key); + } else + ctx->root_structure = v; + break; + case JSON_KEY: + stack = &(ctx->stack[ctx->stack_offset - 1]); + stack->key = memory_calloc(ctx->user_calloc, length + 1, sizeof(char)); + stack->key_length = length; + if (!stack->key) + return JSON_ERROR_NO_MEMORY; + memcpy(stack->key, data, length); + break; + case JSON_STRING: + case JSON_INT: + case JSON_FLOAT: + case JSON_NULL: + case JSON_TRUE: + case JSON_FALSE: + stack = &(ctx->stack[ctx->stack_offset - 1]); + v = ctx->create_data(type, data, length); + ctx->append(stack->val, stack->key, stack->key_length, v); + free(stack->key); + break; + } + return 0; +} diff --git a/src/json.h b/src/json.h new file mode 100644 index 0000000..0c1b5ad --- /dev/null +++ b/src/json.h @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2009 Vincent Hanquez + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; version 2.1 or version 3.0 only. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef JSON_H +#define JSON_H + +#include +#include + +#define JSON_MAJOR 0 +#define JSON_MINOR 7 +#define JSON_VERSION (JSON_MAJOR * 100 + JSON_MINOR) + +typedef enum +{ + JSON_NONE, + JSON_ARRAY_BEGIN, + JSON_OBJECT_BEGIN, + JSON_ARRAY_END, + JSON_OBJECT_END, + JSON_INT, + JSON_FLOAT, + JSON_STRING, + JSON_KEY, + JSON_TRUE, + JSON_FALSE, + JSON_NULL, +} json_type; + +typedef enum +{ + /* SUCCESS = 0 */ + /* running out of memory */ + JSON_ERROR_NO_MEMORY = 1, + /* character < 32, except space newline tab */ + JSON_ERROR_BAD_CHAR, + /* trying to pop more object/array than pushed on the stack */ + JSON_ERROR_POP_EMPTY, + /* trying to pop wrong type of mode. popping array in object mode, vice versa */ + JSON_ERROR_POP_UNEXPECTED_MODE, + /* reach nesting limit on stack */ + JSON_ERROR_NESTING_LIMIT, + /* reach data limit on buffer */ + JSON_ERROR_DATA_LIMIT, + /* comment are not allowed with current configuration */ + JSON_ERROR_COMMENT_NOT_ALLOWED, + /* unexpected char in the current parser context */ + JSON_ERROR_UNEXPECTED_CHAR, + /* unicode low surrogate missing after high surrogate */ + JSON_ERROR_UNICODE_MISSING_LOW_SURROGATE, + /* unicode low surrogate missing without previous high surrogate */ + JSON_ERROR_UNICODE_UNEXPECTED_LOW_SURROGATE, + /* found a comma not in structure (array/object) */ + JSON_ERROR_COMMA_OUT_OF_STRUCTURE, + /* callback returns error */ + JSON_ERROR_CALLBACK, +} json_error; + +#define LIBJSON_DEFAULT_STACK_SIZE 256 +#define LIBJSON_DEFAULT_BUFFER_SIZE 4096 + +typedef int (*json_parser_callback)(void *userdata, int type, const char *data, uint32_t length); +typedef int (*json_printer_callback)(void *userdata, const char *s, uint32_t length); + +typedef struct { + uint32_t buffer_initial_size; + uint32_t max_nesting; + uint32_t max_data; + int allow_c_comments; + int allow_yaml_comments; + void * (*user_calloc)(size_t nmemb, size_t size); + void * (*user_realloc)(void *ptr, size_t size); +} json_config; + +typedef struct json_parser { + json_config config; + + /* SAJ callback */ + json_parser_callback callback; + void *userdata; + + /* parser state */ + uint8_t state; + uint8_t save_state; + uint8_t expecting_key; + uint16_t unicode_multi; + json_type type; + + /* state stack */ + uint8_t *stack; + uint32_t stack_offset; + uint32_t stack_size; + + /* parse buffer */ + char *buffer; + uint32_t buffer_size; + uint32_t buffer_offset; +} json_parser; + +typedef struct json_printer { + json_printer_callback callback; + void *userdata; + + char *indentstr; + int indentlevel; + int afterkey; + int enter_object; + int first; +} json_printer; + +/** json_parser_init initialize a parser structure taking a config, + * a config and its userdata. + * return JSON_ERROR_NO_MEMORY if memory allocation failed or SUCCESS. */ +int json_parser_init(json_parser *parser, json_config *cfg, + json_parser_callback callback, void *userdata); + +/** json_parser_free freed memory structure allocated by the parser */ +int json_parser_free(json_parser *parser); + +/** json_parser_string append a string s with a specific length to the parser + * return 0 if everything went ok, a JSON_ERROR_* otherwise. + * the user can supplied a valid processed pointer that will + * be fill with the number of processed characters before returning */ +int json_parser_string(json_parser *parser, const char *string, + uint32_t length, uint32_t *processed); + +/** json_parser_char append one single char to the parser + * return 0 if everything went ok, a JSON_ERROR_* otherwise */ +int json_parser_char(json_parser *parser, unsigned char next_char); + +/** json_parser_is_done return 0 is the parser isn't in a finish state. !0 if it is */ +int json_parser_is_done(json_parser *parser); + +/** json_print_init initialize a printer context. always succeed */ +int json_print_init(json_printer *printer, json_printer_callback callback, void *userdata); + +/** json_print_free free a printer context + * doesn't do anything now, but in future print_init could allocate memory */ +int json_print_free(json_printer *printer); + +/** json_print_pretty pretty print the passed argument (type/data/length). */ +int json_print_pretty(json_printer *printer, int type, const char *data, uint32_t length); + +/** json_print_raw prints without eye candy the passed argument (type/data/length). */ +int json_print_raw(json_printer *printer, int type, const char *data, uint32_t length); + +/** json_print_args takes multiple types and pass them to the printer function + * array, object and constants doesn't take a string and length argument. + * int, float, key, string need to be followed by a pointer to char and then a length. + * if the length argument is -1, then the strlen function will use on the string argument. + * the function call should always be terminated by -1 */ +int json_print_args(json_printer *, int (*f)(json_printer *, int, const char *, uint32_t), ...); + +/** callback from the parser_dom callback to create object and array */ +typedef void * (*json_parser_dom_create_structure)(int, int); + +/** callback from the parser_dom callback to create data values */ +typedef void * (*json_parser_dom_create_data)(int, const char *, uint32_t); + +/** callback from the parser helper callback to append a value to an object or array value + * append(parent, key, key_length, val); */ +typedef int (*json_parser_dom_append)(void *, char *, uint32_t, void *); + +/** the json_parser_dom permits to create a DOM like tree easily through the + * use of 3 callbacks where the user can choose the representation of the JSON values */ +typedef struct json_parser_dom +{ + /* object stack */ + struct stack_elem { void *val; char *key; uint32_t key_length; } *stack; + uint32_t stack_size; + uint32_t stack_offset; + + /* overridable memory allocator */ + void * (*user_calloc)(size_t nmemb, size_t size); + void * (*user_realloc)(void *ptr, size_t size); + + /* returned root structure (object or array) */ + void *root_structure; + + /* callbacks */ + json_parser_dom_create_structure create_structure; + json_parser_dom_create_data create_data; + json_parser_dom_append append; +} json_parser_dom; + +/** initialize a parser dom structure with the necessary callbacks */ +int json_parser_dom_init(json_parser_dom *helper, + json_parser_dom_create_structure create_structure, + json_parser_dom_create_data create_data, + json_parser_dom_append append); +/** free memory allocated by the DOM callback helper */ +int json_parser_dom_free(json_parser_dom *ctx); + +/** helper to parser callback that arrange parsing events into comprehensive JSON data structure */ +int json_parser_dom_callback(void *userdata, int type, const char *data, uint32_t length); + +#endif /* JSON_H */