diff options
Diffstat (limited to 'util/json.c')
-rw-r--r-- | util/json.c | 407 |
1 files changed, 407 insertions, 0 deletions
diff --git a/util/json.c b/util/json.c new file mode 100644 index 0000000..2978410 --- /dev/null +++ b/util/json.c @@ -0,0 +1,407 @@ +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <stdarg.h> +#include "json.h" + +static inline void fail_and_notify(void) +{ + fprintf(stderr, "Allocation of memory for json object failed, aborting.\n"); + abort(); +} + +struct json_object *json_create_object(void) +{ + void *test = calloc(1, sizeof(struct json_object)); + if (!test) + fail_and_notify(); + return test; +} + +struct json_array *json_create_array(void) +{ + void *test = calloc(1, sizeof(struct json_array)); + if (!test) + fail_and_notify(); + return test; +} + +static struct json_pair *json_create_pair(const char *name, struct json_value *value) +{ + struct json_pair *pair = malloc(sizeof(struct json_pair)); + if (pair) { + pair->name = strdup(name); + pair->value = value; + + value->parent_type = JSON_PARENT_TYPE_PAIR; + value->parent_pair = pair; + } else + fail_and_notify(); + + return pair; +} + +static struct json_value *json_create_value_int(long long number) +{ + struct json_value *value = malloc(sizeof(struct json_value)); + + if (value) { + value->type = JSON_TYPE_INTEGER; + value->integer_number = number; + } else + fail_and_notify(); + + return value; +} + +static struct json_value *json_create_value_uint(unsigned long long number) +{ + struct json_value *value = malloc(sizeof(struct json_value)); + + if (value) { + value->type = JSON_TYPE_UINT; + value->uint_number = number; + } else + fail_and_notify(); + + return value; +} + +static struct json_value *json_create_value_float(long double number) +{ + struct json_value *value = malloc(sizeof(struct json_value)); + + if (value) { + value->type = JSON_TYPE_FLOAT; + value->float_number = number; + } else + fail_and_notify(); + + return value; +} + +static char *strdup_escape(const char *str) +{ + const char *input = str; + char *p, *ret; + int escapes; + + if (!strlen(str)) + return NULL; + + escapes = 0; + while ((input = strpbrk(input, "\\\"")) != NULL) { + escapes++; + input++; + } + + p = ret = malloc(strlen(str) + escapes + 1); + if (!ret) + fail_and_notify(); + + while (*str) { + if (*str == '\\' || *str == '\"') + *p++ = '\\'; + *p++ = *str++; + } + *p = '\0'; + + return ret; +} + +/* + * Valid JSON strings must escape '"' and '/' with a preceding '/' + */ +static struct json_value *json_create_value_string(const char *str) +{ + struct json_value *value = malloc(sizeof(struct json_value)); + + if (value) { + value->type = JSON_TYPE_STRING; + value->string = strdup_escape(str ? str : "(null)"); + if (!value->string) { + free(value); + value = NULL; + return value; + } + } + if (!value) + fail_and_notify(); + + return value; +} + +static struct json_value *json_create_value_object(struct json_object *obj) +{ + struct json_value *value = malloc(sizeof(struct json_value)); + + if (value) { + value->type = JSON_TYPE_OBJECT; + value->object = obj; + obj->parent = value; + } else + fail_and_notify(); + + return value; +} + +static struct json_value *json_create_value_array(struct json_array *array) +{ + struct json_value *value = malloc(sizeof(struct json_value)); + + if (value) { + value->type = JSON_TYPE_ARRAY; + value->array = array; + array->parent = value; + } else + fail_and_notify(); + + return value; +} + +static void json_free_pair(struct json_pair *pair); +static void json_free_value(struct json_value *value); + +void json_free_object(struct json_object *obj) +{ + int i; + + for (i = 0; i < obj->pair_cnt; i++) + json_free_pair(obj->pairs[i]); + free(obj->pairs); + free(obj); +} + +void json_free_array(struct json_array *array) +{ + int i; + + for (i = 0; i < array->value_cnt; i++) + json_free_value(array->values[i]); + free(array->values); + free(array); +} + +static void json_free_pair(struct json_pair *pair) +{ + json_free_value(pair->value); + free(pair->name); + free(pair); +} + +static void json_free_value(struct json_value *value) +{ + switch (value->type) { + case JSON_TYPE_STRING: + free(value->string); + break; + case JSON_TYPE_OBJECT: + json_free_object(value->object); + break; + case JSON_TYPE_ARRAY: + json_free_array(value->array); + break; + } + free(value); +} + +static int json_array_add_value(struct json_array *array, struct json_value *value) +{ + struct json_value **values = realloc(array->values, + sizeof(struct json_value *) * (array->value_cnt + 1)); + + if (!values) + return ENOMEM; + values[array->value_cnt] = value; + array->value_cnt++; + array->values = values; + + value->parent_type = JSON_PARENT_TYPE_ARRAY; + value->parent_array = array; + return 0; +} + +static int json_object_add_pair(struct json_object *obj, struct json_pair *pair) +{ + struct json_pair **pairs = realloc(obj->pairs, + sizeof(struct json_pair *) * (obj->pair_cnt + 1)); + if (!pairs) + return ENOMEM; + pairs[obj->pair_cnt] = pair; + obj->pair_cnt++; + obj->pairs = pairs; + + pair->parent = obj; + return 0; +} + +int json_object_add_value_type(struct json_object *obj, const char *name, int type, ...) +{ + struct json_value *value; + struct json_pair *pair; + va_list args; + int ret; + + va_start(args, type); + if (type == JSON_TYPE_STRING) + value = json_create_value_string(va_arg(args, char *)); + else if (type == JSON_TYPE_INTEGER) + value = json_create_value_int(va_arg(args, long long)); + else if (type == JSON_TYPE_UINT) + value = json_create_value_uint(va_arg(args, unsigned long long)); + else if (type == JSON_TYPE_FLOAT) + value = json_create_value_float(va_arg(args, long double)); + else if (type == JSON_TYPE_OBJECT) + value = json_create_value_object(va_arg(args, struct json_object *)); + else + value = json_create_value_array(va_arg(args, struct json_array *)); + va_end(args); + + if (!value) + return ENOMEM; + + pair = json_create_pair(name, value); + if (!pair) { + json_free_value(value); + return ENOMEM; + } + ret = json_object_add_pair(obj, pair); + if (ret) { + json_free_pair(pair); + return ENOMEM; + } + return 0; +} + +static void json_print_array(struct json_array *array, void *); +int json_array_add_value_type(struct json_array *array, int type, ...) +{ + struct json_value *value; + va_list args; + int ret; + + va_start(args, type); + if (type == JSON_TYPE_STRING) + value = json_create_value_string(va_arg(args, char *)); + else if (type == JSON_TYPE_INTEGER) + value = json_create_value_int(va_arg(args, long long)); + else if (type == JSON_TYPE_UINT) + value = json_create_value_uint(va_arg(args, unsigned long long)); + else if (type == JSON_TYPE_FLOAT) + value = json_create_value_float(va_arg(args, double)); + else if (type == JSON_TYPE_OBJECT) + value = json_create_value_object(va_arg(args, struct json_object *)); + else + value = json_create_value_array(va_arg(args, struct json_array *)); + va_end(args); + + if (!value) + return ENOMEM; + + ret = json_array_add_value(array, value); + if (ret) { + json_free_value(value); + return ENOMEM; + } + return 0; +} + +static int json_value_level(struct json_value *value); +static int json_pair_level(struct json_pair *pair); +static int json_array_level(struct json_array *array); +static int json_object_level(struct json_object *object) +{ + if (object->parent == NULL) + return 0; + return json_value_level(object->parent); +} + +static int json_pair_level(struct json_pair *pair) +{ + return json_object_level(pair->parent) + 1; +} + +static int json_array_level(struct json_array *array) +{ + return json_value_level(array->parent); +} + +static int json_value_level(struct json_value *value) +{ + if (value->parent_type == JSON_PARENT_TYPE_PAIR) + return json_pair_level(value->parent_pair); + else + return json_array_level(value->parent_array) + 1; +} + +static void json_print_level(int level, void *out) +{ + while (level-- > 0) + printf(" "); +} + +static void json_print_pair(struct json_pair *pair, void *); +static void json_print_array(struct json_array *array, void *); +static void json_print_value(struct json_value *value, void *); +void json_print_object(struct json_object *obj, void *out) +{ + int i; + + printf("{\n"); + for (i = 0; i < obj->pair_cnt; i++) { + if (i > 0) + printf(",\n"); + json_print_pair(obj->pairs[i], out); + } + printf("\n"); + json_print_level(json_object_level(obj), out); + printf("}"); +} + +static void json_print_pair(struct json_pair *pair, void *out) +{ + json_print_level(json_pair_level(pair), out); + printf("\"%s\" : ", pair->name); + json_print_value(pair->value, out); +} + +static void json_print_array(struct json_array *array, void *out) +{ + int i; + + printf("[\n"); + for (i = 0; i < array->value_cnt; i++) { + if (i > 0) + printf(",\n"); + json_print_level(json_value_level(array->values[i]), out); + json_print_value(array->values[i], out); + } + printf("\n"); + json_print_level(json_array_level(array), out); + printf("]"); +} + +static void json_print_value(struct json_value *value, void *out) +{ + switch (value->type) { + case JSON_TYPE_STRING: + printf( "\"%s\"", value->string); + break; + case JSON_TYPE_INTEGER: + printf( "%lld", value->integer_number); + break; + case JSON_TYPE_UINT: + printf( "%llu", value->uint_number); + break; + case JSON_TYPE_FLOAT: + printf( "%.0Lf", value->float_number); + break; + case JSON_TYPE_OBJECT: + json_print_object(value->object, out); + break; + case JSON_TYPE_ARRAY: + json_print_array(value->array, out); + break; + } +} |