-
-
Save jabb/4109122 to your computer and use it in GitHub Desktop.
JSON in C source file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Copyright (c) 2012, Michael Patraw | |
* All rights reserved. | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions are met: | |
* * Redistributions of source code must retain the above copyright | |
* notice, this list of conditions and the following disclaimer. | |
* * Redistributions in binary form must reproduce the above copyright | |
* notice, this list of conditions and the following disclaimer in the | |
* documentation and/or other materials provided with the distribution. | |
* * The name of Michael Patraw may not be used to endorse or promote | |
* products derived from this software without specific prior written | |
* permission. | |
* | |
* THIS SOFTWARE IS PROVIDED BY Michael Patraw ''AS IS'' AND ANY | |
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
* DISCLAIMED. IN NO EVENT SHALL Michael Patraw BE LIABLE FOR ANY | |
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
*/ | |
#include "jc.h" | |
#include <ctype.h> | |
#include <stdarg.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include "tree.h" | |
#define MAX(a, b) (((a) > (b)) ? (a) : (b)) | |
/******************************************************************************\ | |
\******************************************************************************/ | |
struct jc_obj_node | |
{ | |
RB_ENTRY(jc_obj_node) entry; | |
char *key; | |
struct jc_atom *value; | |
}; | |
static int jc_obj_node_cmp(struct jc_obj_node *a, struct jc_obj_node *b) | |
{ | |
return strcmp(a->key, b->key); | |
} | |
RB_HEAD(jc_obj, jc_obj_node); | |
RB_GENERATE_STATIC(jc_obj, jc_obj_node, entry, jc_obj_node_cmp) | |
/******************************************************************************\ | |
\******************************************************************************/ | |
struct jc_arr | |
{ | |
struct jc_atom **arr; | |
int allocd; | |
int length; | |
}; | |
static int arr_resize(struct jc_arr *arr, int newsize) | |
{ | |
struct jc_atom **tmp; | |
if (!arr->arr) | |
{ | |
arr->arr = malloc(sizeof *arr * newsize); | |
if (!arr->arr) | |
return -1; | |
arr->allocd = newsize; | |
} | |
else | |
{ | |
tmp = realloc(arr->arr, sizeof *arr * newsize); | |
if (!tmp) | |
return -1; | |
arr->arr = tmp; | |
arr->allocd = newsize; | |
} | |
return 0; | |
} | |
static int arr_insert(struct jc_arr *arr, int pos, struct jc_atom *atom) | |
{ | |
int rv, i; | |
if (arr->length >= arr->allocd) | |
{ | |
if (arr->allocd == 0) | |
arr->allocd = 1; | |
if ((rv = arr_resize(arr, arr->allocd * 2))) | |
return rv; | |
} | |
if (pos >= arr->allocd) | |
{ | |
if (arr->allocd == 0) | |
arr->allocd = 1; | |
if ((rv = arr_resize(arr, MAX(arr->allocd * 2, pos + 1)))) | |
return rv; | |
} | |
if (pos >= 0 && pos < arr->length) | |
{ | |
for (i = arr->length; i > pos; --i) | |
arr->arr[i] = arr->arr[i - 1]; | |
arr->arr[pos] = atom; | |
arr->length++; | |
} | |
else | |
{ | |
for (i = arr->length; i < pos; ++i) | |
{ | |
arr->arr[i] = jc_null(); | |
if (!arr->arr[i]) | |
return -1; | |
arr->length++; | |
} | |
arr->arr[pos] = atom; | |
arr->length++; | |
} | |
return 0; | |
} | |
static int arr_remove(struct jc_arr *arr, int pos) | |
{ | |
int rv, i; | |
if (arr->length < (arr->allocd >> 1)) | |
{ | |
if ((rv = arr_resize(arr, arr->allocd >> 1))) | |
return rv; | |
} | |
for (i = pos; i < arr->length; ++i) | |
arr->arr[i] = arr->arr[i + 1]; | |
arr->arr[i] = NULL; | |
arr->length--; | |
return 0; | |
} | |
static struct jc_atom *arr_get(struct jc_arr *arr, int pos) | |
{ | |
if (pos >= 0 && pos < arr->length) | |
return arr->arr[pos]; | |
return NULL; | |
} | |
/******************************************************************************\ | |
\******************************************************************************/ | |
static char *string_duplicate(const char *str) | |
{ | |
char *dup = malloc(strlen(str) + 1); | |
if (!dup) | |
return NULL; | |
strcpy(dup, str); | |
return dup; | |
} | |
static char *string_append(char *a, const char *b) | |
{ | |
char *s = realloc(a, strlen(a) + strlen(b) + 1); | |
if (!s) | |
return NULL; | |
strcat(s, b); | |
return s; | |
} | |
static char *string_expand(char *dest, const char *src) | |
{ | |
char c; | |
while ((c = *(src++))) | |
{ | |
switch (c) | |
{ | |
case '\"': | |
*(dest++) = '\\'; | |
*(dest++) = '\"'; | |
break; | |
case '\\': | |
*(dest++) = '\\'; | |
*(dest++) = '\\'; | |
break; | |
case '\b': | |
*(dest++) = '\\'; | |
*(dest++) = 'b'; | |
break; | |
case '\f': | |
*(dest++) = '\\'; | |
*(dest++) = 'f'; | |
break; | |
case '\n': | |
*(dest++) = '\\'; | |
*(dest++) = 'n'; | |
break; | |
case '\r': | |
*(dest++) = '\\'; | |
*(dest++) = 'r'; | |
break; | |
case '\t': | |
*(dest++) = '\\'; | |
*(dest++) = 't'; | |
break; | |
default: | |
*(dest++) = c; | |
break; | |
} | |
} | |
*dest = '\0'; | |
return dest; | |
} | |
static char *string_expand_alloc(const char *src) | |
{ | |
char *dest = malloc(2 * strlen(src) + 1); | |
string_expand(dest, src); | |
return dest; | |
} | |
/******************************************************************************\ | |
\******************************************************************************/ | |
static struct jc_atom *jc_atom(int type, ...) | |
{ | |
va_list args; | |
struct jc_atom *atom; | |
va_start(args, type); | |
atom = malloc(sizeof *atom); | |
if (!atom) | |
goto failure; | |
atom->type = type; | |
atom->ref = 0; | |
atom->iter = NULL; | |
switch (type) | |
{ | |
case JC_BOOLEAN: | |
atom->guts.boolean = va_arg(args, int); | |
break; | |
case JC_NUMBER: | |
atom->guts.number = va_arg(args, double); | |
break; | |
case JC_STRING: | |
atom->guts.string = string_duplicate(va_arg(args, const char *)); | |
if (!atom->guts.string) | |
goto failure; | |
break; | |
case JC_OBJECT: | |
atom->guts.object = malloc(sizeof(struct jc_obj)); | |
if (!atom->guts.object) | |
goto failure; | |
memset(atom->guts.object, 0, sizeof(struct jc_obj)); | |
break; | |
case JC_ARRAY: | |
atom->guts.array = malloc(sizeof(struct jc_arr)); | |
memset(atom->guts.array, 0, sizeof(struct jc_arr)); | |
break; | |
default: break; | |
} | |
va_end(args); | |
return atom; | |
failure: | |
va_end(args); | |
if (atom) | |
free(atom); | |
return NULL; | |
} | |
struct jc_atom *jc_null(void) | |
{ | |
return jc_atom(JC_NULL); | |
} | |
struct jc_atom *jc_boolean(int tf) | |
{ | |
return jc_atom(JC_BOOLEAN, tf); | |
} | |
struct jc_atom *jc_number(double num) | |
{ | |
return jc_atom(JC_NUMBER, num); | |
} | |
struct jc_atom *jc_string(const char *str) | |
{ | |
return jc_atom(JC_STRING, str); | |
} | |
struct jc_atom *jc_object(void) | |
{ | |
return jc_atom(JC_OBJECT); | |
} | |
struct jc_atom *jc_array(void) | |
{ | |
return jc_atom(JC_ARRAY); | |
} | |
struct jc_atom *jc_unref(struct jc_atom *atom) | |
{ | |
int i; | |
struct jc_obj_node *node; | |
struct jc_obj_node *next; | |
if (--atom->ref > 0) | |
return atom; | |
switch (atom->type) | |
{ | |
case JC_STRING: | |
free(atom->guts.string); | |
break; | |
case JC_OBJECT: | |
for (node = RB_MIN(jc_obj, atom->guts.object); node != NULL; node = next) | |
{ | |
next = RB_NEXT(jc_obj, atom->guts.object, node); | |
RB_REMOVE(jc_obj, atom->guts.object, node); | |
jc_unref(node->value); | |
free(node->key); | |
free(node); | |
} | |
free(atom->guts.object); | |
break; | |
case JC_ARRAY: | |
for (i = 0; i < ((struct jc_arr *)atom->guts.array)->length; ++i) | |
jc_unref(((struct jc_arr *)atom->guts.array)->arr[i]); | |
if (((struct jc_arr *)atom->guts.array)->arr) | |
free(((struct jc_arr *)atom->guts.array)->arr); | |
free(atom->guts.array); | |
break; | |
default: break; | |
} | |
free(atom); | |
return NULL; | |
} | |
struct jc_atom *jc_ref(struct jc_atom *atom) | |
{ | |
atom->ref++; | |
return atom; | |
} | |
int jc_type(struct jc_atom *atom) | |
{ | |
if (!atom) | |
return JC_NULL; | |
return atom->type; | |
} | |
void *jc_value(struct jc_atom *atom) | |
{ | |
switch (atom->type) | |
{ | |
case JC_BOOLEAN: | |
return &atom->guts.boolean; | |
case JC_NUMBER: | |
return &atom->guts.number; | |
case JC_STRING: | |
return atom->guts.string; | |
default: | |
return NULL; | |
} | |
} | |
int jc_length(struct jc_atom *atom) | |
{ | |
int len = 0; | |
struct jc_obj_node *node; | |
struct jc_obj_node *next; | |
switch (atom->type) | |
{ | |
case JC_OBJECT: | |
for (node = RB_MIN(jc_obj, atom->guts.object); node != NULL; node = next) | |
{ | |
next = RB_NEXT(jc_obj, atom->guts.object, node); | |
len++; | |
} | |
return len; | |
case JC_ARRAY: | |
return ((struct jc_arr *)atom->guts.array)->length; | |
default: return -1; | |
} | |
} | |
struct jc_atom *jc_set(struct jc_atom *obj, struct jc_atom *newatom, ...) | |
{ | |
va_list args; | |
const char *key; | |
int index; | |
struct jc_obj_node *node = NULL, *toremove, find; | |
struct jc_atom *existing = NULL; | |
if (obj->type == JC_OBJECT) | |
{ | |
va_start(args, newatom); | |
key = va_arg(args, const char *); | |
va_end(args); | |
existing = jc_get(obj, key); | |
if (existing || !newatom) | |
{ | |
find.key = (char *)key; | |
toremove = RB_FIND(jc_obj, obj->guts.object, &find); | |
if (toremove) | |
{ | |
RB_REMOVE(jc_obj, obj->guts.object, toremove); | |
jc_unref(toremove->value); | |
free(toremove->key); | |
free(toremove); | |
} | |
if (!newatom) | |
return NULL; | |
} | |
jc_ref(newatom); | |
node = malloc(sizeof *node); | |
if (!node) | |
goto failure; | |
node->key = string_duplicate(key); | |
if (!node->key) | |
goto failure; | |
node->value = newatom; | |
RB_INSERT(jc_obj, obj->guts.object, node); | |
return newatom; | |
} | |
else if (obj->type == JC_ARRAY) | |
{ | |
va_start(args, newatom); | |
index = va_arg(args, int); | |
va_end(args); | |
existing = arr_get(obj->guts.array, index); | |
if (existing || !newatom) | |
{ | |
jc_unref(arr_get(obj->guts.array, index)); | |
arr_remove(obj->guts.array, index); | |
if (!newatom) | |
return NULL; | |
} | |
jc_ref(newatom); | |
if (arr_insert(obj->guts.array, index, newatom)) | |
goto failure; | |
return newatom; | |
} | |
else | |
{ | |
goto failure; | |
} | |
failure: | |
if (newatom) | |
jc_unref(newatom); | |
if (node) | |
free(node); | |
return NULL; | |
} | |
struct jc_atom *jc_get(struct jc_atom *obj, ...) | |
{ | |
va_list args; | |
int index; | |
struct jc_obj_node find; | |
struct jc_obj_node *node; | |
if (obj->type == JC_OBJECT) | |
{ | |
va_start(args, obj); | |
find.key = (char *)va_arg(args, const char *); | |
va_end(args); | |
node = RB_FIND(jc_obj, obj->guts.object, &find); | |
if (node) | |
return node->value; | |
else | |
return NULL; | |
} | |
else if (obj->type == JC_ARRAY) | |
{ | |
va_start(args, obj); | |
index = va_arg(args, int); | |
va_end(args); | |
if (index == -1) | |
index = jc_length(obj) - 1; | |
return arr_get(obj->guts.array, index); | |
} | |
else | |
{ | |
return NULL; | |
} | |
} | |
struct jc_atom *jc_vset(struct jc_atom *obj, struct jc_atom *newatom, const char *key) | |
{ | |
int index; | |
struct jc_atom *find; | |
char *sub = NULL; | |
const char *end; | |
while (obj && *key) | |
{ | |
if (obj->type == JC_OBJECT) | |
{ | |
end = strstr(key, "."); | |
if (!end) | |
break; | |
sub = malloc(end - key + 1); | |
if (!sub) | |
return NULL; | |
strncpy(sub, key, end - key); | |
sub[end - key] = '\0'; | |
find = jc_get(obj, sub); | |
if (find) | |
obj = find; | |
else | |
return NULL; | |
} | |
else if (obj->type == JC_ARRAY) | |
{ | |
end = strstr(key, "."); | |
if (!end) | |
break; | |
sub = malloc(end - key + 1); | |
if (!sub) | |
return NULL; | |
strncpy(sub, key, end - key); | |
sub[end - key] = '\0'; | |
sscanf(sub, "%d", &index); | |
if (index == -1) | |
index = jc_length(obj) - 1; | |
obj = arr_get(obj->guts.array, index); | |
} | |
else | |
{ | |
return NULL; | |
} | |
if (end && *end) | |
key = end + 1; | |
else if (end) | |
key = end; | |
if (sub) | |
free(sub); | |
} | |
return jc_set(obj, newatom, key); | |
} | |
struct jc_atom *jc_vget(struct jc_atom *obj, const char *key) | |
{ | |
int index; | |
struct jc_atom *find; | |
char *sub = NULL; | |
const char *end; | |
while (obj && *key) | |
{ | |
if (obj->type == JC_OBJECT) | |
{ | |
end = strstr(key, "."); | |
if (!end) | |
end = key + strlen(key); | |
sub = malloc(end - key + 1); | |
if (!sub) | |
return NULL; | |
strncpy(sub, key, end - key); | |
sub[end - key] = '\0'; | |
find = jc_get(obj, sub); | |
if (find) | |
obj = find; | |
else | |
return NULL; | |
} | |
else if (obj->type == JC_ARRAY) | |
{ | |
end = strstr(key, "."); | |
if (!end) | |
end = key + strlen(key); | |
sub = malloc(end - key + 1); | |
if (!sub) | |
return NULL; | |
strncpy(sub, key, end - key); | |
sub[end - key] = '\0'; | |
sscanf(sub, "%d", &index); | |
if (index == -1) | |
index = jc_length(obj) - 1; | |
obj = arr_get(obj->guts.array, index); | |
} | |
else | |
{ | |
return NULL; | |
} | |
if (end && *end) | |
key = end + 1; | |
else if (end) | |
key = end; | |
if (sub) | |
free(sub); | |
} | |
return obj; | |
} | |
int jc_iter(struct jc_atom *obj, void *key, struct jc_atom **val) | |
{ | |
if (obj->type == JC_OBJECT) | |
{ | |
if (!obj->iter) | |
{ | |
obj->iter = RB_MIN(jc_obj, obj->guts.object); | |
} | |
else | |
{ | |
obj->iter = RB_NEXT(jc_obj, obj->guts.object, obj->iter); | |
} | |
if (obj->iter) | |
{ | |
if (key) | |
*((const char **)key) = ((struct jc_obj_node *)obj->iter)->key; | |
if (val) | |
*val = ((struct jc_obj_node *)obj->iter)->value; | |
return 1; | |
} | |
else | |
{ | |
return 0; | |
} | |
} | |
else if (obj->type == JC_ARRAY) | |
{ | |
if (!obj->iter) | |
{ | |
obj->iter = (void *)1; | |
} | |
else | |
{ | |
obj->iter = (void *)(((long)obj->iter) + 1); | |
} | |
if (obj->iter) | |
{ | |
if ((long)obj->iter > ((struct jc_arr *)obj->guts.array)->length) | |
{ | |
obj->iter = NULL; | |
return 0; | |
} | |
else | |
{ | |
if (key) | |
*((int *)key) = ((long)obj->iter) - 1; | |
if (val) | |
*val = ((struct jc_arr *)obj->guts.array)->arr[((long)obj->iter) - 1]; | |
return 1; | |
} | |
} | |
else | |
{ | |
return 0; | |
} | |
} | |
else | |
{ | |
return 0; | |
} | |
} | |
static struct jc_atom *_jc_from_string(const char **str); | |
static int _skip(const char **str, const char *tokens) | |
{ | |
int match; | |
int i; | |
int len = strlen(tokens); | |
while (**str) | |
{ | |
match = 0; | |
for (i = 0; i < len; ++i) | |
{ | |
if (**str == tokens[i]) | |
match = 1; | |
} | |
if (!match) | |
return 1; | |
(*str)++; | |
} | |
return 0; | |
} | |
static int _skip_once(const char **str, const char *tokens, char once) | |
{ | |
int match; | |
int i; | |
int len = strlen(tokens); | |
while (**str) | |
{ | |
if (**str == once) | |
{ | |
(*str)++; | |
if (!**str) | |
return 0; | |
else | |
return 1; | |
} | |
match = 0; | |
for (i = 0; i < len; ++i) | |
{ | |
if (**str == tokens[i]) | |
match = 1; | |
} | |
if (!match) | |
return 1; | |
(*str)++; | |
} | |
return 0; | |
} | |
static struct jc_atom *_jc_parse_null(const char **str) | |
{ | |
if (strstr(*str, "null") == *str) | |
{ | |
*str += 4; | |
return jc_null(); | |
} | |
else | |
{ | |
return NULL; | |
} | |
} | |
static struct jc_atom *_jc_parse_boolean(const char **str) | |
{ | |
if (strstr(*str, "true") == *str) | |
{ | |
*str += 4; | |
return jc_boolean(1); | |
} | |
else if (strstr(*str, "false") == *str) | |
{ | |
*str += 5; | |
return jc_boolean(0); | |
} | |
else | |
{ | |
return NULL; | |
} | |
} | |
static struct jc_atom *_jc_parse_number(const char **str) | |
{ | |
double number; | |
const char *end = *str; | |
while (isdigit(*end) || *end == '.' || *end == 'e' || *end == 'E' || *end == '+' || *end == '-') | |
{ | |
end++; | |
} | |
sscanf(*str, "%lf", &number); | |
*str = end; | |
return jc_number(number); | |
} | |
static struct jc_atom *_jc_parse_string(const char **str) | |
{ | |
struct jc_atom *atom = NULL; | |
int i = 0; | |
int mallocd = 1024; | |
char *buffer = malloc(mallocd), *tmp; | |
if (!buffer) | |
goto failure; | |
memset(buffer, 0, mallocd); | |
_skip_once(str, " \t\n\r", '"'); | |
while (**str) | |
{ | |
if (**str == '"') | |
break; | |
if (**str == '\\' && *((*str) + 1) && *((*str) + 1) != 'u') | |
{ | |
(*str)++; | |
if (!**str) | |
break; | |
if (**str == '"') | |
buffer[i] = '"'; | |
else if (**str == '\\') | |
buffer[i] = '\\'; | |
else if (**str == 'b') | |
buffer[i] = '\b'; | |
else if (**str == 'f') | |
buffer[i] = '\f'; | |
else if (**str == 'n') | |
buffer[i] = '\n'; | |
else if (**str == 'r') | |
buffer[i] = '\r'; | |
else if (**str == 't') | |
buffer[i] = '\t'; | |
else | |
buffer[i] = **str; | |
} | |
else | |
{ | |
buffer[i] = **str; | |
} | |
buffer[i + 1] = 0; | |
(*str)++; | |
i++; | |
if (i + 1 >= mallocd) | |
{ | |
mallocd *= 2; | |
tmp = realloc(buffer, mallocd); | |
if (!tmp) | |
goto failure; | |
buffer = tmp; | |
} | |
} | |
_skip_once(str, " \t\n\r", '"'); | |
atom = jc_string(buffer); | |
if (!atom) | |
goto failure; | |
free(buffer); | |
return atom; | |
failure: | |
if (buffer) | |
free(buffer); | |
return NULL; | |
} | |
static struct jc_atom *_jc_parse_object(const char **str) | |
{ | |
struct jc_atom *key = NULL; | |
struct jc_atom *atom = jc_object(), *elem; | |
if (!atom) | |
goto failure; | |
_skip_once(str, " \t\n\r", '{'); | |
while (**str) | |
{ | |
if (!_skip(str, " \t\n\r,")) | |
break; | |
if (**str == '}') | |
break; | |
key = _jc_parse_string(str); | |
if (jc_type(key) != JC_STRING) | |
goto failure; | |
if (!_skip(str, " \t\n\r:")) | |
break; | |
elem = _jc_from_string(str); | |
if (!elem) | |
goto failure; | |
if (!jc_set(atom, elem, jc_value(key))) | |
goto failure; | |
jc_unref(key); | |
} | |
_skip_once(str, " \t\n\r", '}'); | |
return atom; | |
failure: | |
if (key) | |
jc_unref(key); | |
if (atom) | |
jc_unref(atom); | |
return NULL; | |
} | |
static struct jc_atom *_jc_parse_array(const char **str) | |
{ | |
struct jc_atom *atom = jc_array(), *elem; | |
if (!atom) | |
goto failure; | |
_skip_once(str, " \t\n\r", '['); | |
while (**str) | |
{ | |
if (!_skip(str, " \t\n\r,")) | |
break; | |
if (**str == ']') | |
break; | |
elem = _jc_from_string(str); | |
if (!elem) | |
goto failure; | |
if (!jc_set(atom, elem, jc_length(atom))) | |
goto failure; | |
} | |
_skip_once(str, " \t\n\r", ']'); | |
return atom; | |
failure: | |
if (atom) | |
jc_unref(atom); | |
return NULL; | |
} | |
static struct jc_atom *_jc_from_string(const char **str) | |
{ | |
int c; | |
while ((c = **str)) | |
{ | |
switch (c) | |
{ | |
case ' ': case '\t': case '\n': case '\r': | |
break; | |
case 'n': | |
return _jc_parse_null(str); | |
case 't': case 'f': | |
return _jc_parse_boolean(str); | |
case '-': | |
case '0': case '1': case '2': case '3': case '4': | |
case '5': case '6': case '7': case '8': case '9': | |
return _jc_parse_number(str); | |
case '"': | |
return _jc_parse_string(str); | |
case '{': | |
return _jc_parse_object(str); | |
case '[': | |
return _jc_parse_array(str); | |
default: | |
return NULL; | |
} | |
if (!**str) | |
break; | |
(*str)++; | |
} | |
return NULL; | |
} | |
struct jc_atom *jc_from_string(const char *str) | |
{ | |
return _jc_from_string(&str); | |
} | |
static char *_jc_to_string(struct jc_atom *atom, char *str) | |
{ | |
int i; | |
struct jc_obj_node *node; | |
struct jc_obj_node *next; | |
char buffer[1024]; | |
char *newstr = str, *tmpstr = NULL; | |
switch (atom->type) | |
{ | |
case JC_NULL: | |
newstr = string_append(str, "null"); | |
if (!newstr) | |
goto failure; | |
break; | |
case JC_BOOLEAN: | |
newstr = string_append(str, atom->guts.boolean ? "true" : "false"); | |
if (!newstr) | |
goto failure; | |
break; | |
case JC_NUMBER: | |
sprintf(buffer, "%g", atom->guts.number); | |
newstr = string_append(str, buffer); | |
if (!newstr) | |
goto failure; | |
break; | |
case JC_STRING: | |
newstr = string_append(str, "\""); | |
if (!newstr) | |
goto failure; | |
str = newstr; | |
tmpstr = string_expand_alloc(atom->guts.string); | |
if (!tmpstr) | |
goto failure; | |
newstr = string_append(str, tmpstr); | |
if (!newstr) | |
goto failure; | |
free(tmpstr); | |
tmpstr = NULL; | |
str = newstr; | |
newstr = string_append(str, "\""); | |
if (!newstr) | |
goto failure; | |
str = newstr; | |
break; | |
case JC_OBJECT: | |
newstr = string_append(str, "{"); | |
if (!newstr) | |
goto failure; | |
str = newstr; | |
for (node = RB_MIN(jc_obj, atom->guts.object); node != NULL; node = next) | |
{ | |
next = RB_NEXT(jc_obj, atom->guts.object, node); | |
newstr = string_append(str, "\""); | |
if (!newstr) | |
goto failure; | |
str = newstr; | |
tmpstr = string_expand_alloc(node->key); | |
if (!tmpstr) | |
goto failure; | |
newstr = string_append(str, tmpstr); | |
if (!newstr) | |
goto failure; | |
free(tmpstr); | |
tmpstr = NULL; | |
str = newstr; | |
newstr = string_append(str, "\":"); | |
if (!newstr) | |
goto failure; | |
str = newstr; | |
str = _jc_to_string(node->value, str); | |
if (!str) | |
goto failure; | |
if (next != NULL) | |
{ | |
newstr = string_append(str, ","); | |
if (!newstr) | |
goto failure; | |
str = newstr; | |
} | |
} | |
newstr = string_append(str, "}"); | |
if (!newstr) | |
goto failure; | |
break; | |
case JC_ARRAY: | |
newstr = string_append(str, "["); | |
if (!newstr) | |
goto failure; | |
str = newstr; | |
for (i = 0; i < jc_length(atom); ++i) | |
{ | |
str = _jc_to_string(jc_get(atom, i), str); | |
if (!str) | |
goto failure; | |
if (i + 1 < jc_length(atom)) | |
{ | |
newstr = string_append(str, ","); | |
if (!newstr) | |
goto failure; | |
str = newstr; | |
} | |
} | |
newstr = string_append(str, "]"); | |
if (!newstr) | |
goto failure; | |
break; | |
default: break; | |
} | |
return newstr; | |
failure: | |
if (str) | |
free(str); | |
if (tmpstr) | |
free(tmpstr); | |
return NULL; | |
} | |
char *jc_to_string(struct jc_atom *atom) | |
{ | |
return _jc_to_string(atom, string_duplicate("")); | |
} | |
static void _jc_print(FILE *fd, struct jc_atom *atom, int tablevel) | |
{ | |
int i, j; | |
struct jc_obj_node *node; | |
struct jc_obj_node *next; | |
char *tmpstr; | |
switch (atom->type) | |
{ | |
case JC_NULL: | |
fprintf(fd, "null"); | |
break; | |
case JC_BOOLEAN: | |
fprintf(fd, atom->guts.boolean ? "true" : "false"); | |
break; | |
case JC_NUMBER: | |
fprintf(fd, "%g", atom->guts.number); | |
break; | |
case JC_STRING: | |
tmpstr = string_expand_alloc(atom->guts.string); | |
if (!tmpstr) | |
return; | |
fprintf(fd, "\"%s\"", tmpstr); | |
free(tmpstr); | |
break; | |
case JC_OBJECT: | |
fprintf(fd, "{\n"); | |
for (node = RB_MIN(jc_obj, atom->guts.object); node != NULL; node = next) | |
{ | |
next = RB_NEXT(jc_obj, atom->guts.object, node); | |
tmpstr = string_expand_alloc(node->key); | |
if (!tmpstr) | |
return; | |
for (j = 0; j < tablevel + 1; ++j) | |
fprintf(fd, "\t"); | |
fprintf(fd, "\"%s\": ", tmpstr); | |
free(tmpstr); | |
_jc_print(fd, node->value, tablevel + 1); | |
if (next != NULL) | |
fprintf(fd, ",\n"); | |
else | |
fprintf(fd, "\n"); | |
} | |
for (j = 0; j < tablevel; ++j) | |
fprintf(fd, "\t"); | |
fprintf(fd, "}"); | |
break; | |
case JC_ARRAY: | |
fprintf(fd, "[\n"); | |
for (i = 0; i < jc_length(atom); ++i) | |
{ | |
for (j = 0; j < tablevel + 1; ++j) | |
fprintf(fd, "\t"); | |
_jc_print(fd, jc_get(atom, i), tablevel + 1); | |
if (i + 1 < jc_length(atom)) | |
fprintf(fd, ",\n"); | |
else | |
fprintf(fd, "\n"); | |
} | |
for (j = 0; j < tablevel; ++j) | |
fprintf(fd, "\t"); | |
fprintf(fd, "]"); | |
break; | |
default: break; | |
} | |
} | |
void jc_print(void *fd, struct jc_atom *atom) | |
{ | |
_jc_print(fd, atom, 0); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment