parent
07700218b7
commit
3f763edaed
|
@ -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
|
||||
|
|
401
src/e_mod_main.c
401
src/e_mod_main.c
|
@ -4,8 +4,10 @@
|
|||
|
||||
#include <Evry.h>
|
||||
#include <curl/curl.h>
|
||||
#include <E_Notify.h>
|
||||
|
||||
#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();
|
||||
|
|
|
@ -0,0 +1,963 @@
|
|||
/*
|
||||
* Copyright (C) 2009 Vincent Hanquez <vincent@snarc.org>
|
||||
*
|
||||
* 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include "json.h"
|
||||
|
||||
#ifdef TRACING_ENABLE
|
||||
#include <stdio.h>
|
||||
#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;
|
||||
}
|
|
@ -0,0 +1,207 @@
|
|||
/*
|
||||
* Copyright (C) 2009 Vincent Hanquez <vincent@snarc.org>
|
||||
*
|
||||
* 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 <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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 */
|
Loading…
Reference in New Issue