diff --git a/readme.md b/readme.md index fdeb7e8..f5afd87 100644 --- a/readme.md +++ b/readme.md @@ -147,4 +147,9 @@ sudo ./sdmon /dev/mmcblk0 "success":true } ``` + +## Licenses + +This project uses the [json](https://github.com/json-parser/json-parser) and [json-builder](https://github.com/json-parser/json-builder) libraries, licensed under the BSD-2-Clause License. Their .c and .h source files have simply been added untouched to the source tree, as suggested in their readme file. The headers of these files contain more details about the license. + (c) 2018 - today, **OGI-IT**, Ognian Tschakalov and contributors, released under GNU GPL v2 diff --git a/src/Makefile b/src/Makefile index 5f7cc9a..be91d60 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2,10 +2,10 @@ #VERSION = 0.01 CC = /usr/bin/gcc -CFLAGS = -Wall -g -D_REENTRANT -DVERSION="\"$(VERSION_STRING)\"" -static -LDFLAGS = +CFLAGS = -Wall -g -D_REENTRANT -DVERSION="\"$(VERSION_STRING)\"" -I. -static +LDFLAGS = -lm -OBJ = sdmon.o +OBJ = sdmon.o json.o json-builder.o BIN = sdmon $(BIN): $(OBJ) diff --git a/src/Makefile.freebsd b/src/Makefile.freebsd index f533943..c1b064b 100644 --- a/src/Makefile.freebsd +++ b/src/Makefile.freebsd @@ -2,10 +2,10 @@ #VERSION = 0.01 CC = /usr/bin/cc -CFLAGS = -Wall -g -D_REENTRANT -DVERSION="\"$(VERSION_STRING)\"" -static -LDFLAGS = +CFLAGS = -Wall -g -D_REENTRANT -DVERSION="\"$(VERSION_STRING)\"" -I. -static +LDFLAGS = -lm -OBJ = sdmon.o +OBJ = sdmon.o json.o json-builder.o BIN = sdmon $(BIN): $(OBJ) diff --git a/src/json-builder.c b/src/json-builder.c new file mode 100644 index 0000000..2b96e6d --- /dev/null +++ b/src/json-builder.c @@ -0,0 +1,995 @@ + +/* vim: set et ts=3 sw=3 sts=3 ft=c: + * + * Copyright (C) 2014 James McLaughlin. All rights reserved. + * https://github.com/udp/json-builder + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 THE AUTHOR OR CONTRIBUTORS 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 "json-builder.h" + +#include +#include +#include +#include + +#ifdef _MSC_VER + #define snprintf _snprintf +#endif + +static const json_serialize_opts default_opts = +{ + json_serialize_mode_single_line, + 0, + 3 /* indent_size */ +}; + +typedef struct json_builder_value +{ + json_value value; + + int is_builder_value; + + size_t additional_length_allocated; + size_t length_iterated; + +} json_builder_value; + +static int builderize (json_value * value) +{ + if (((json_builder_value *) value)->is_builder_value) + return 1; + + if (value->type == json_object) + { + unsigned int i; + + /* Values straight out of the parser have the names of object entries + * allocated in the same allocation as the values array itself. This is + * not desirable when manipulating values because the names would be easy + * to clobber. + */ + for (i = 0; i < value->u.object.length; ++ i) + { + json_char * name_copy; + json_object_entry * entry = &value->u.object.values [i]; + + if (! (name_copy = (json_char *) malloc ((entry->name_length + 1) * sizeof (json_char)))) + return 0; + + memcpy (name_copy, entry->name, entry->name_length + 1); + entry->name = name_copy; + } + } + + ((json_builder_value *) value)->is_builder_value = 1; + + return 1; +} + +const size_t json_builder_extra = sizeof(json_builder_value) - sizeof(json_value); + +/* These flags are set up from the opts before serializing to make the + * serializer conditions simpler. + */ +const int f_spaces_around_brackets = (1 << 0); +const int f_spaces_after_commas = (1 << 1); +const int f_spaces_after_colons = (1 << 2); +const int f_tabs = (1 << 3); + +static int get_serialize_flags (json_serialize_opts opts) +{ + int flags = 0; + + if (opts.mode == json_serialize_mode_packed) + return 0; + + if (opts.mode == json_serialize_mode_multiline) + { + if (opts.opts & json_serialize_opt_use_tabs) + flags |= f_tabs; + } + else + { + if (! (opts.opts & json_serialize_opt_pack_brackets)) + flags |= f_spaces_around_brackets; + + if (! (opts.opts & json_serialize_opt_no_space_after_comma)) + flags |= f_spaces_after_commas; + } + + if (! (opts.opts & json_serialize_opt_no_space_after_colon)) + flags |= f_spaces_after_colons; + + return flags; +} + +json_value * json_array_new (size_t length) +{ + json_value * value = (json_value *) calloc (1, sizeof (json_builder_value)); + + if (!value) + return NULL; + + ((json_builder_value *) value)->is_builder_value = 1; + + value->type = json_array; + + if (! (value->u.array.values = (json_value **) malloc (length * sizeof (json_value *)))) + { + free (value); + return NULL; + } + + ((json_builder_value *) value)->additional_length_allocated = length; + + return value; +} + +json_value * json_array_push (json_value * array, json_value * value) +{ + assert (array->type == json_array); + + if (!builderize (array) || !builderize (value)) + return NULL; + + if (((json_builder_value *) array)->additional_length_allocated > 0) + { + -- ((json_builder_value *) array)->additional_length_allocated; + } + else + { + json_value ** values_new = (json_value **) realloc + (array->u.array.values, sizeof (json_value *) * (array->u.array.length + 1)); + + if (!values_new) + return NULL; + + array->u.array.values = values_new; + } + + array->u.array.values [array->u.array.length] = value; + ++ array->u.array.length; + + value->parent = array; + + return value; +} + +json_value * json_object_new (size_t length) +{ + json_value * value = (json_value *) calloc (1, sizeof (json_builder_value)); + + if (!value) + return NULL; + + ((json_builder_value *) value)->is_builder_value = 1; + + value->type = json_object; + + if (! (value->u.object.values = (json_object_entry *) calloc + (length, sizeof (*value->u.object.values)))) + { + free (value); + return NULL; + } + + ((json_builder_value *) value)->additional_length_allocated = length; + + return value; +} + +json_value * json_object_push (json_value * object, + const json_char * name, + json_value * value) +{ + return json_object_push_length (object, strlen (name), name, value); +} + +json_value * json_object_push_length (json_value * object, + unsigned int name_length, const json_char * name, + json_value * value) +{ + json_char * name_copy; + + assert (object->type == json_object); + + if (! (name_copy = (json_char *) malloc ((name_length + 1) * sizeof (json_char)))) + return NULL; + + memcpy (name_copy, name, name_length * sizeof (json_char)); + name_copy [name_length] = 0; + + if (!json_object_push_nocopy (object, name_length, name_copy, value)) + { + free (name_copy); + return NULL; + } + + return value; +} + +json_value * json_object_push_nocopy (json_value * object, + unsigned int name_length, json_char * name, + json_value * value) +{ + json_object_entry * entry; + + assert (object->type == json_object); + + if (!builderize (object) || !builderize (value)) + return NULL; + + if (((json_builder_value *) object)->additional_length_allocated > 0) + { + -- ((json_builder_value *) object)->additional_length_allocated; + } + else + { + json_object_entry * values_new = (json_object_entry *) + realloc (object->u.object.values, sizeof (*object->u.object.values) + * (object->u.object.length + 1)); + + if (!values_new) + return NULL; + + object->u.object.values = values_new; + } + + entry = object->u.object.values + object->u.object.length; + + entry->name_length = name_length; + entry->name = name; + entry->value = value; + + ++ object->u.object.length; + + value->parent = object; + + return value; +} + +json_value * json_string_new (const json_char * buf) +{ + return json_string_new_length (strlen (buf), buf); +} + +json_value * json_string_new_length (unsigned int length, const json_char * buf) +{ + json_value * value; + json_char * copy = (json_char *) malloc ((length + 1) * sizeof (json_char)); + + if (!copy) + return NULL; + + memcpy (copy, buf, length * sizeof (json_char)); + copy [length] = 0; + + if (! (value = json_string_new_nocopy (length, copy))) + { + free (copy); + return NULL; + } + + return value; +} + +json_value * json_string_new_nocopy (unsigned int length, json_char * buf) +{ + json_value * value = (json_value *) calloc (1, sizeof (json_builder_value)); + + if (!value) + return NULL; + + ((json_builder_value *) value)->is_builder_value = 1; + + value->type = json_string; + value->u.string.length = length; + value->u.string.ptr = buf; + + return value; +} + +json_value * json_integer_new (json_int_t integer) +{ + json_value * value = (json_value *) calloc (1, sizeof (json_builder_value)); + + if (!value) + return NULL; + + ((json_builder_value *) value)->is_builder_value = 1; + + value->type = json_integer; + value->u.integer = integer; + + return value; +} + +json_value * json_double_new (double dbl) +{ + json_value * value = (json_value *) calloc (1, sizeof (json_builder_value)); + + if (!value) + return NULL; + + ((json_builder_value *) value)->is_builder_value = 1; + + value->type = json_double; + value->u.dbl = dbl; + + return value; +} + +json_value * json_boolean_new (int b) +{ + json_value * value = (json_value *) calloc (1, sizeof (json_builder_value)); + + if (!value) + return NULL; + + ((json_builder_value *) value)->is_builder_value = 1; + + value->type = json_boolean; + value->u.boolean = b; + + return value; +} + +json_value * json_null_new (void) +{ + json_value * value = (json_value *) calloc (1, sizeof (json_builder_value)); + + if (!value) + return NULL; + + ((json_builder_value *) value)->is_builder_value = 1; + + value->type = json_null; + + return value; +} + +void json_object_sort (json_value * object, json_value * proto) +{ + unsigned int i, out_index = 0; + + if (!builderize (object)) + return; /* TODO error */ + + assert (object->type == json_object); + assert (proto->type == json_object); + + for (i = 0; i < proto->u.object.length; ++ i) + { + unsigned int j; + json_object_entry proto_entry = proto->u.object.values [i]; + + for (j = 0; j < object->u.object.length; ++ j) + { + json_object_entry entry = object->u.object.values [j]; + + if (entry.name_length != proto_entry.name_length) + continue; + + if (memcmp (entry.name, proto_entry.name, entry.name_length) != 0) + continue; + + object->u.object.values [j] = object->u.object.values [out_index]; + object->u.object.values [out_index] = entry; + + ++ out_index; + } + } +} + +json_value * json_object_merge (json_value * objectA, json_value * objectB) +{ + unsigned int i; + + assert (objectA->type == json_object); + assert (objectB->type == json_object); + assert (objectA != objectB); + + if (!builderize (objectA) || !builderize (objectB)) + return NULL; + + if (objectB->u.object.length <= + ((json_builder_value *) objectA)->additional_length_allocated) + { + ((json_builder_value *) objectA)->additional_length_allocated + -= objectB->u.object.length; + } + else + { + json_object_entry * values_new; + + unsigned int alloc = + objectA->u.object.length + + ((json_builder_value *) objectA)->additional_length_allocated + + objectB->u.object.length; + + if (! (values_new = (json_object_entry *) + realloc (objectA->u.object.values, sizeof (json_object_entry) * alloc))) + { + return NULL; + } + + objectA->u.object.values = values_new; + } + + for (i = 0; i < objectB->u.object.length; ++ i) + { + json_object_entry * entry = &objectA->u.object.values[objectA->u.object.length + i]; + + *entry = objectB->u.object.values[i]; + entry->value->parent = objectA; + } + + objectA->u.object.length += objectB->u.object.length; + + free (objectB->u.object.values); + free (objectB); + + return objectA; +} + +static size_t measure_string (unsigned int length, + const json_char * str) +{ + unsigned int i; + size_t measured_length = 0; + + for(i = 0; i < length; ++ i) + { + json_char c = str [i]; + + switch (c) + { + case '"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + + measured_length += 2; + break; + + default: + + ++ measured_length; + break; + }; + }; + + return measured_length; +} + +#define PRINT_ESCAPED(c) do { \ + *buf ++ = '\\'; \ + *buf ++ = (c); \ +} while(0); \ + +static size_t serialize_string (json_char * buf, + unsigned int length, + const json_char * str) +{ + json_char * orig_buf = buf; + unsigned int i; + + for(i = 0; i < length; ++ i) + { + json_char c = str [i]; + + switch (c) + { + case '"': PRINT_ESCAPED ('\"'); continue; + case '\\': PRINT_ESCAPED ('\\'); continue; + case '\b': PRINT_ESCAPED ('b'); continue; + case '\f': PRINT_ESCAPED ('f'); continue; + case '\n': PRINT_ESCAPED ('n'); continue; + case '\r': PRINT_ESCAPED ('r'); continue; + case '\t': PRINT_ESCAPED ('t'); continue; + + default: + + *buf ++ = c; + break; + }; + }; + + return buf - orig_buf; +} + +size_t json_measure (json_value * value) +{ + return json_measure_ex (value, default_opts); +} + +#define MEASURE_NEWLINE() do { \ + ++ newlines; \ + indents += depth; \ +} while(0); \ + +size_t json_measure_ex (json_value * value, json_serialize_opts opts) +{ + size_t total = 1; /* null terminator */ + size_t newlines = 0; + size_t depth = 0; + size_t indents = 0; + int flags; + int bracket_size, comma_size, colon_size; + + flags = get_serialize_flags (opts); + + /* to reduce branching + */ + bracket_size = flags & f_spaces_around_brackets ? 2 : 1; + comma_size = flags & f_spaces_after_commas ? 2 : 1; + colon_size = flags & f_spaces_after_colons ? 2 : 1; + + while (value) + { + json_int_t integer; + json_object_entry * entry; + + switch (value->type) + { + case json_array: + + if (((json_builder_value *) value)->length_iterated == 0) + { + if (value->u.array.length == 0) + { + total += 2; /* `[]` */ + break; + } + + total += bracket_size; /* `[` */ + + ++ depth; + MEASURE_NEWLINE(); /* \n after [ */ + } + + if (((json_builder_value *) value)->length_iterated == value->u.array.length) + { + -- depth; + MEASURE_NEWLINE(); + total += bracket_size; /* `]` */ + + ((json_builder_value *) value)->length_iterated = 0; + break; + } + + if (((json_builder_value *) value)->length_iterated > 0) + { + total += comma_size; /* `, ` */ + + MEASURE_NEWLINE(); + } + + ((json_builder_value *) value)->length_iterated++; + value = value->u.array.values [((json_builder_value *) value)->length_iterated - 1]; + continue; + + case json_object: + + if (((json_builder_value *) value)->length_iterated == 0) + { + if (value->u.object.length == 0) + { + total += 2; /* `{}` */ + break; + } + + total += bracket_size; /* `{` */ + + ++ depth; + MEASURE_NEWLINE(); /* \n after { */ + } + + if (((json_builder_value *) value)->length_iterated == value->u.object.length) + { + -- depth; + MEASURE_NEWLINE(); + total += bracket_size; /* `}` */ + + ((json_builder_value *) value)->length_iterated = 0; + break; + } + + if (((json_builder_value *) value)->length_iterated > 0) + { + total += comma_size; /* `, ` */ + MEASURE_NEWLINE(); + } + + entry = value->u.object.values + (((json_builder_value *) value)->length_iterated ++); + + total += 2 + colon_size; /* `"": ` */ + total += measure_string (entry->name_length, entry->name); + + value = entry->value; + continue; + + case json_string: + + total += 2; /* `""` */ + total += measure_string (value->u.string.length, value->u.string.ptr); + break; + + case json_integer: + + integer = value->u.integer; + + if (integer < 0) + { + total += 1; /* `-` */ + integer = - integer; + } + + ++ total; /* first digit */ + + while (integer >= 10) + { + ++ total; /* another digit */ + integer /= 10; + } + + break; + + case json_double: + + total += snprintf (NULL, 0, "%g", value->u.dbl); + + /* Because sometimes we need to add ".0" if sprintf does not do it + * for us. Downside is that we allocate more bytes than strictly + * needed for serialization. + */ + total += 2; + + break; + + case json_boolean: + + total += value->u.boolean ? + 4: /* `true` */ + 5; /* `false` */ + + break; + + case json_null: + + total += 4; /* `null` */ + break; + + default: + break; + }; + + value = value->parent; + } + + if (opts.mode == json_serialize_mode_multiline) + { + total += newlines * (((opts.opts & json_serialize_opt_CRLF) ? 2 : 1) + opts.indent_size); + total += indents * opts.indent_size; + } + + return total; +} + +void json_serialize (json_char * buf, json_value * value) +{ + json_serialize_ex (buf, value, default_opts); +} + +#define PRINT_NEWLINE() do { \ + if (opts.mode == json_serialize_mode_multiline) { \ + if (opts.opts & json_serialize_opt_CRLF) \ + *buf ++ = '\r'; \ + *buf ++ = '\n'; \ + for(i = 0; i < indent; ++ i) \ + *buf ++ = indent_char; \ + } \ +} while(0); \ + +#define PRINT_OPENING_BRACKET(c) do { \ + *buf ++ = (c); \ + if (flags & f_spaces_around_brackets) \ + *buf ++ = ' '; \ +} while(0); \ + +#define PRINT_CLOSING_BRACKET(c) do { \ + if (flags & f_spaces_around_brackets) \ + *buf ++ = ' '; \ + *buf ++ = (c); \ +} while(0); \ + +void json_serialize_ex (json_char * buf, json_value * value, json_serialize_opts opts) +{ + json_int_t integer, orig_integer; + json_object_entry * entry; + json_char * ptr, * dot; + int indent = 0; + char indent_char; + int i; + int flags; + + flags = get_serialize_flags (opts); + + indent_char = flags & f_tabs ? '\t' : ' '; + + while (value) + { + switch (value->type) + { + case json_array: + + if (((json_builder_value *) value)->length_iterated == 0) + { + if (value->u.array.length == 0) + { + *buf ++ = '['; + *buf ++ = ']'; + + break; + } + + PRINT_OPENING_BRACKET ('['); + + indent += opts.indent_size; + PRINT_NEWLINE(); + } + + if (((json_builder_value *) value)->length_iterated == value->u.array.length) + { + indent -= opts.indent_size; + PRINT_NEWLINE(); + PRINT_CLOSING_BRACKET (']'); + + ((json_builder_value *) value)->length_iterated = 0; + break; + } + + if (((json_builder_value *) value)->length_iterated > 0) + { + *buf ++ = ','; + + if (flags & f_spaces_after_commas) + *buf ++ = ' '; + + PRINT_NEWLINE(); + } + + ((json_builder_value *) value)->length_iterated++; + value = value->u.array.values [((json_builder_value *) value)->length_iterated - 1]; + continue; + + case json_object: + + if (((json_builder_value *) value)->length_iterated == 0) + { + if (value->u.object.length == 0) + { + *buf ++ = '{'; + *buf ++ = '}'; + + break; + } + + PRINT_OPENING_BRACKET ('{'); + + indent += opts.indent_size; + PRINT_NEWLINE(); + } + + if (((json_builder_value *) value)->length_iterated == value->u.object.length) + { + indent -= opts.indent_size; + PRINT_NEWLINE(); + PRINT_CLOSING_BRACKET ('}'); + + ((json_builder_value *) value)->length_iterated = 0; + break; + } + + if (((json_builder_value *) value)->length_iterated > 0) + { + *buf ++ = ','; + + if (flags & f_spaces_after_commas) + *buf ++ = ' '; + + PRINT_NEWLINE(); + } + + entry = value->u.object.values + (((json_builder_value *) value)->length_iterated ++); + + *buf ++ = '\"'; + buf += serialize_string (buf, entry->name_length, entry->name); + *buf ++ = '\"'; + *buf ++ = ':'; + + if (flags & f_spaces_after_colons) + *buf ++ = ' '; + + value = entry->value; + continue; + + case json_string: + + *buf ++ = '\"'; + buf += serialize_string (buf, value->u.string.length, value->u.string.ptr); + *buf ++ = '\"'; + break; + + case json_integer: + + integer = value->u.integer; + + if (integer < 0) + { + *buf ++ = '-'; + integer = - integer; + } + + orig_integer = integer; + + ++ buf; + + while (integer >= 10) + { + ++ buf; + integer /= 10; + } + + integer = orig_integer; + ptr = buf; + + do + { + *-- ptr = "0123456789"[integer % 10]; + + } while ((integer /= 10) > 0); + + break; + + case json_double: + + ptr = buf; + + buf += sprintf (buf, "%g", value->u.dbl); + + if ((dot = strchr (ptr, ','))) + { + *dot = '.'; + } + else if (!strchr (ptr, '.') && !strchr (ptr, 'e')) + { + *buf ++ = '.'; + *buf ++ = '0'; + } + + break; + + case json_boolean: + + if (value->u.boolean) + { + memcpy (buf, "true", 4); + buf += 4; + } + else + { + memcpy (buf, "false", 5); + buf += 5; + } + + break; + + case json_null: + + memcpy (buf, "null", 4); + buf += 4; + break; + + default: + break; + }; + + value = value->parent; + } + + *buf = 0; +} + +void json_builder_free (json_value * value) +{ + json_value * cur_value; + + if (!value) + return; + + value->parent = 0; + + while (value) + { + switch (value->type) + { + case json_array: + + if (!value->u.array.length) + { + free (value->u.array.values); + break; + } + + value = value->u.array.values [-- value->u.array.length]; + continue; + + case json_object: + + if (!value->u.object.length) + { + free (value->u.object.values); + break; + } + + -- value->u.object.length; + + if (((json_builder_value *) value)->is_builder_value) + { + /* Names are allocated separately for builder values. In parser + * values, they are part of the same allocation as the values array + * itself. + */ + free (value->u.object.values [value->u.object.length].name); + } + + value = value->u.object.values [value->u.object.length].value; + continue; + + case json_string: + + free (value->u.string.ptr); + break; + + default: + break; + }; + + cur_value = value; + value = value->parent; + free (cur_value); + } +} + + + + + + diff --git a/src/json-builder.h b/src/json-builder.h new file mode 100644 index 0000000..29a36ff --- /dev/null +++ b/src/json-builder.h @@ -0,0 +1,159 @@ + +/* vim: set et ts=3 sw=3 sts=3 ft=c: + * + * Copyright (C) 2014 James McLaughlin. All rights reserved. + * https://github.com/udp/json-builder + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 THE AUTHOR OR CONTRIBUTORS 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. + */ + +#ifndef _JSON_BUILDER_H +#define _JSON_BUILDER_H + +/* Requires json.h from json-parser + * https://github.com/udp/json-parser + */ +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* IMPORTANT NOTE: If you want to use json-builder functions with values + * allocated by json-parser as part of the parsing process, you must pass + * json_builder_extra as the value_extra setting in json_settings when + * parsing. Otherwise there will not be room for the extra state and + * json-builder WILL invoke undefined behaviour. + * + * Also note that unlike json-parser, json-builder does not currently support + * custom allocators (for no particular reason other than that it doesn't have + * any settings or global state.) + */ +extern const size_t json_builder_extra; + + +/*** Arrays + *** + * Note that all of these length arguments are just a hint to allow for + * pre-allocation - passing 0 is fine. + */ +json_value * json_array_new (size_t length); +json_value * json_array_push (json_value * array, json_value *); + + +/*** Objects + ***/ +json_value * json_object_new (size_t length); + +json_value * json_object_push (json_value * object, + const json_char * name, + json_value *); + +/* Same as json_object_push, but doesn't call strlen() for you. + */ +json_value * json_object_push_length (json_value * object, + unsigned int name_length, const json_char * name, + json_value *); + +/* Same as json_object_push_length, but doesn't copy the name buffer before + * storing it in the value. Use this micro-optimisation at your own risk. + */ +json_value * json_object_push_nocopy (json_value * object, + unsigned int name_length, json_char * name, + json_value *); + +/* Merges all entries from objectB into objectA and destroys objectB. + */ +json_value * json_object_merge (json_value * objectA, json_value * objectB); + +/* Sort the entries of an object based on the order in a prototype object. + * Helpful when reading JSON and writing it again to preserve user order. + */ +void json_object_sort (json_value * object, json_value * proto); + + + +/*** Strings + ***/ +json_value * json_string_new (const json_char *); +json_value * json_string_new_length (unsigned int length, const json_char *); +json_value * json_string_new_nocopy (unsigned int length, json_char *); + + +/*** Everything else + ***/ +json_value * json_integer_new (json_int_t); +json_value * json_double_new (double); +json_value * json_boolean_new (int); +json_value * json_null_new (void); + + +/*** Serializing + ***/ +#define json_serialize_mode_multiline 0 +#define json_serialize_mode_single_line 1 +#define json_serialize_mode_packed 2 + +#define json_serialize_opt_CRLF (1 << 1) +#define json_serialize_opt_pack_brackets (1 << 2) +#define json_serialize_opt_no_space_after_comma (1 << 3) +#define json_serialize_opt_no_space_after_colon (1 << 4) +#define json_serialize_opt_use_tabs (1 << 5) + +typedef struct json_serialize_opts +{ + int mode; + int opts; + int indent_size; + +} json_serialize_opts; + + +/* Returns a length in characters that is at least large enough to hold the + * value in its serialized form, including a null terminator. + */ +size_t json_measure (json_value *); +size_t json_measure_ex (json_value *, json_serialize_opts); + + +/* Serializes a JSON value into the buffer given (which must already be + * allocated with a length of at least json_measure(value, opts)) + */ +void json_serialize (json_char * buf, json_value *); +void json_serialize_ex (json_char * buf, json_value *, json_serialize_opts); + + +/*** Cleaning up + ***/ +void json_builder_free (json_value *); + +#ifdef __cplusplus +} +#endif + +#endif + + + diff --git a/src/json.c b/src/json.c new file mode 100644 index 0000000..deef49f --- /dev/null +++ b/src/json.c @@ -0,0 +1,1057 @@ +/* vim: set et ts=3 sw=3 sts=3 ft=c: + * + * Copyright (C) 2012-2021 the json-parser authors All rights reserved. + * https://github.com/json-parser/json-parser + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 THE AUTHOR OR CONTRIBUTORS 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 "json.h" + +#ifdef _MSC_VER + #ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS + #endif +#endif + +#include +#include +#include +#include +#include +#include + +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + /* C99 might give us uintptr_t and UINTPTR_MAX but they also might not be provided */ + #include +#endif + +#ifndef JSON_INT_T_OVERRIDDEN + #if defined(_MSC_VER) + /* https://docs.microsoft.com/en-us/cpp/cpp/data-type-ranges */ + #define JSON_INT_MAX 9223372036854775807LL + #elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + /* C99 */ + #define JSON_INT_MAX INT_FAST64_MAX + #else + /* C89 */ + #include + #define JSON_INT_MAX LONG_MAX + #endif +#endif + +#ifndef JSON_INT_MAX +#define JSON_INT_MAX (json_int_t)(((unsigned json_int_t)(-1)) / (unsigned json_int_t)2); +#endif + +typedef unsigned int json_uchar; + +const struct _json_value json_value_none; + +static unsigned char hex_value (json_char c) +{ + if (isdigit((unsigned char)c)) + return c - '0'; + + switch (c) { + case 'a': case 'A': return 0x0A; + case 'b': case 'B': return 0x0B; + case 'c': case 'C': return 0x0C; + case 'd': case 'D': return 0x0D; + case 'e': case 'E': return 0x0E; + case 'f': case 'F': return 0x0F; + default: return 0xFF; + } +} + +static int would_overflow (json_int_t value, json_char b) +{ + return ((JSON_INT_MAX - (b - '0')) / 10 ) < value; +} + +typedef struct +{ + size_t used_memory; + + json_settings settings; + int first_pass; + + const json_char * ptr; + unsigned int cur_line, cur_col; + +} json_state; + +static void * default_alloc (size_t size, int zero, void * user_data) +{ + (void)user_data; /* ignore unused-parameter warn */ + return zero ? calloc (1, size) : malloc (size); +} + +static void default_free (void * ptr, void * user_data) +{ + (void)user_data; /* ignore unused-parameter warn */ + free (ptr); +} + +static void * json_alloc (json_state * state, size_t size, int zero) +{ + if ((ULONG_MAX - 8 - state->used_memory) < size) + return 0; + + if (state->settings.max_memory + && (state->used_memory += size) > state->settings.max_memory) + { + return 0; + } + + return state->settings.mem_alloc (size, zero, state->settings.user_data); +} + +static int new_value (json_state * state, + json_value ** top, json_value ** root, json_value ** alloc, + json_type type) +{ + json_value * value; + size_t values_size; + + if (!state->first_pass) + { + value = *top = *alloc; + *alloc = (*alloc)->_reserved.next_alloc; + + if (!*root) + *root = value; + + switch (value->type) + { + case json_array: + + if (value->u.array.length == 0) + break; + + if (! (value->u.array.values = (json_value **) json_alloc + (state, value->u.array.length * sizeof (json_value *), 0)) ) + { + return 0; + } + + value->u.array.length = 0; + break; + + case json_object: + + if (value->u.object.length == 0) + break; + + values_size = sizeof (*value->u.object.values) * value->u.object.length; + + if (! (value->u.object.values = (json_object_entry *) json_alloc + #ifdef UINTPTR_MAX + (state, values_size + ((uintptr_t) value->u.object.values), 0)) ) + #else + (state, values_size + ((size_t) value->u.object.values), 0)) ) + #endif + { + return 0; + } + + value->_reserved.object_mem = (void *) (((char *) value->u.object.values) + values_size); + + value->u.object.length = 0; + break; + + case json_string: + + if (! (value->u.string.ptr = (json_char *) json_alloc + (state, (value->u.string.length + 1) * sizeof (json_char), 0)) ) + { + return 0; + } + + value->u.string.length = 0; + break; + + default: + break; + }; + + return 1; + } + + if (! (value = (json_value *) json_alloc + (state, sizeof (json_value) + state->settings.value_extra, 1))) + { + return 0; + } + + if (!*root) + *root = value; + + value->type = type; + value->parent = *top; + + #ifdef JSON_TRACK_SOURCE + value->line = state->cur_line; + value->col = state->cur_col; + #endif + + if (*alloc) + (*alloc)->_reserved.next_alloc = value; + + *alloc = *top = value; + + return 1; +} + +#define whitespace \ + case '\n': ++ state.cur_line; state.cur_col = 0; /* FALLTHRU */ \ + case ' ': /* FALLTHRU */ case '\t': /* FALLTHRU */ case '\r' + +#define string_add(b) \ + do { if (!state.first_pass) string [string_length] = b; ++ string_length; } while (0); + +#define line_and_col \ + state.cur_line, state.cur_col + +static const long + flag_next = 1 << 0, + flag_reproc = 1 << 1, + flag_need_comma = 1 << 2, + flag_seek_value = 1 << 3, + flag_escaped = 1 << 4, + flag_string = 1 << 5, + flag_need_colon = 1 << 6, + flag_done = 1 << 7, + flag_num_negative = 1 << 8, + flag_num_zero = 1 << 9, + flag_num_e = 1 << 10, + flag_num_e_got_sign = 1 << 11, + flag_num_e_negative = 1 << 12, + flag_line_comment = 1 << 13, + flag_block_comment = 1 << 14, + flag_num_got_decimal = 1 << 15; + +json_value * json_parse_ex (json_settings * settings, + const json_char * json, + size_t length, + char * error_buf) +{ + char error [json_error_max]; + const json_char * end; + json_value * top, * root, * alloc = 0; + json_state state = { 0 }; + long flags = 0; + int num_digits = 0; + double num_e = 0, num_fraction = 0; + + /* Skip UTF-8 BOM + */ + if (length >= 3 && ((unsigned char) json [0]) == 0xEF + && ((unsigned char) json [1]) == 0xBB + && ((unsigned char) json [2]) == 0xBF) + { + json += 3; + length -= 3; + } + + error[0] = '\0'; + end = (json + length); + + memcpy (&state.settings, settings, sizeof (json_settings)); + + if (!state.settings.mem_alloc) + state.settings.mem_alloc = default_alloc; + + if (!state.settings.mem_free) + state.settings.mem_free = default_free; + + for (state.first_pass = 1; state.first_pass >= 0; -- state.first_pass) + { + json_uchar uchar; + unsigned char uc_b1, uc_b2, uc_b3, uc_b4; + json_char * string = 0; + unsigned int string_length = 0; + + top = root = 0; + flags = flag_seek_value; + + state.cur_line = 1; + + for (state.ptr = json ;; ++ state.ptr) + { + json_char b = (state.ptr == end ? 0 : *state.ptr); + + if (flags & flag_string) + { + if (!b) + { sprintf (error, "%u:%u: Unexpected EOF in string", line_and_col); + goto e_failed; + } + + if (string_length > UINT_MAX - 8) + goto e_overflow; + + if (flags & flag_escaped) + { + flags &= ~ flag_escaped; + + switch (b) + { + case 'b': string_add ('\b'); break; + case 'f': string_add ('\f'); break; + case 'n': string_add ('\n'); break; + case 'r': string_add ('\r'); break; + case 't': string_add ('\t'); break; + case 'u': + + if (end - state.ptr <= 4 || + (uc_b1 = hex_value (*++ state.ptr)) == 0xFF || + (uc_b2 = hex_value (*++ state.ptr)) == 0xFF || + (uc_b3 = hex_value (*++ state.ptr)) == 0xFF || + (uc_b4 = hex_value (*++ state.ptr)) == 0xFF) + { + sprintf (error, "%u:%u: Invalid character value `%c`", line_and_col, b); + goto e_failed; + } + + uc_b1 = (uc_b1 << 4) | uc_b2; + uc_b2 = (uc_b3 << 4) | uc_b4; + uchar = (uc_b1 << 8) | uc_b2; + + if ((uchar & 0xF800) == 0xD800) { + json_uchar uchar2; + + if (end - state.ptr <= 6 || (*++ state.ptr) != '\\' || (*++ state.ptr) != 'u' || + (uc_b1 = hex_value (*++ state.ptr)) == 0xFF || + (uc_b2 = hex_value (*++ state.ptr)) == 0xFF || + (uc_b3 = hex_value (*++ state.ptr)) == 0xFF || + (uc_b4 = hex_value (*++ state.ptr)) == 0xFF) + { + sprintf (error, "%u:%u: Invalid character value `%c`", line_and_col, b); + goto e_failed; + } + + uc_b1 = (uc_b1 << 4) | uc_b2; + uc_b2 = (uc_b3 << 4) | uc_b4; + uchar2 = (uc_b1 << 8) | uc_b2; + + uchar = 0x010000 | ((uchar & 0x3FF) << 10) | (uchar2 & 0x3FF); + } + + if (sizeof (json_char) >= sizeof (json_uchar) || (uchar <= 0x7F)) + { + string_add ((json_char) uchar); + break; + } + + if (uchar <= 0x7FF) + { + if (state.first_pass) + string_length += 2; + else + { string [string_length ++] = 0xC0 | (uchar >> 6); + string [string_length ++] = 0x80 | (uchar & 0x3F); + } + + break; + } + + if (uchar <= 0xFFFF) { + if (state.first_pass) + string_length += 3; + else + { string [string_length ++] = 0xE0 | (uchar >> 12); + string [string_length ++] = 0x80 | ((uchar >> 6) & 0x3F); + string [string_length ++] = 0x80 | (uchar & 0x3F); + } + + break; + } + + if (state.first_pass) + string_length += 4; + else + { string [string_length ++] = 0xF0 | (uchar >> 18); + string [string_length ++] = 0x80 | ((uchar >> 12) & 0x3F); + string [string_length ++] = 0x80 | ((uchar >> 6) & 0x3F); + string [string_length ++] = 0x80 | (uchar & 0x3F); + } + + break; + + default: + string_add (b); + }; + + continue; + } + + if (b == '\\') + { + flags |= flag_escaped; + continue; + } + + if (b == '"') + { + if (!state.first_pass) + string [string_length] = 0; + + flags &= ~ flag_string; + string = 0; + + switch (top->type) + { + case json_string: + + top->u.string.length = string_length; + flags |= flag_next; + + break; + + case json_object: + + if (state.first_pass) { + json_char **chars = (json_char **) &top->u.object.values; + chars[0] += string_length + 1; + } + else + { + top->u.object.values [top->u.object.length].name + = (json_char *) top->_reserved.object_mem; + + top->u.object.values [top->u.object.length].name_length + = string_length; + + (*(json_char **) &top->_reserved.object_mem) += string_length + 1; + } + + flags |= flag_seek_value | flag_need_colon; + continue; + + default: + break; + }; + } + else + { + string_add (b); + continue; + } + } + + if (state.settings.settings & json_enable_comments) + { + if (flags & (flag_line_comment | flag_block_comment)) + { + if (flags & flag_line_comment) + { + if (b == '\r' || b == '\n' || !b) + { + flags &= ~ flag_line_comment; + -- state.ptr; /* so null can be reproc'd */ + } + + continue; + } + + if (flags & flag_block_comment) + { + if (!b) + { sprintf (error, "%u:%u: Unexpected EOF in block comment", line_and_col); + goto e_failed; + } + + if (b == '*' && state.ptr < (end - 1) && state.ptr [1] == '/') + { + flags &= ~ flag_block_comment; + ++ state.ptr; /* skip closing sequence */ + } + + continue; + } + } + else if (b == '/') + { + if (! (flags & (flag_seek_value | flag_done)) && top->type != json_object) + { sprintf (error, "%u:%u: Comment not allowed here", line_and_col); + goto e_failed; + } + + if (++ state.ptr == end) + { sprintf (error, "%u:%u: EOF unexpected", line_and_col); + goto e_failed; + } + + switch (b = *state.ptr) + { + case '/': + flags |= flag_line_comment; + continue; + + case '*': + flags |= flag_block_comment; + continue; + + default: + sprintf (error, "%u:%u: Unexpected `%c` in comment opening sequence", line_and_col, b); + goto e_failed; + }; + } + } + + if (flags & flag_done) + { + if (!b) + break; + + switch (b) + { + whitespace: + continue; + + default: + + sprintf (error, "%u:%u: Trailing garbage: `%c`", + line_and_col, b); + + goto e_failed; + }; + } + + if (flags & flag_seek_value) + { + switch (b) + { + whitespace: + continue; + + case ']': + + if (top && top->type == json_array) + flags = (flags & ~ (flag_need_comma | flag_seek_value)) | flag_next; + else + { sprintf (error, "%u:%u: Unexpected `]`", line_and_col); + goto e_failed; + } + + break; + + default: + + if (flags & flag_need_comma) + { + if (b == ',') + { flags &= ~ flag_need_comma; + continue; + } + else + { + sprintf (error, "%u:%u: Expected `,` before `%c`", + line_and_col, b); + + goto e_failed; + } + } + + if (flags & flag_need_colon) + { + if (b == ':') + { flags &= ~ flag_need_colon; + continue; + } + else + { + sprintf (error, "%u:%u: Expected `:` before `%c`", + line_and_col, b); + + goto e_failed; + } + } + + flags &= ~ flag_seek_value; + + switch (b) + { + case '{': + + if (!new_value (&state, &top, &root, &alloc, json_object)) + goto e_alloc_failure; + + continue; + + case '[': + + if (!new_value (&state, &top, &root, &alloc, json_array)) + goto e_alloc_failure; + + flags |= flag_seek_value; + continue; + + case '"': + + if (!new_value (&state, &top, &root, &alloc, json_string)) + goto e_alloc_failure; + + flags |= flag_string; + + string = top->u.string.ptr; + string_length = 0; + + continue; + + case 't': + + if ((end - state.ptr) <= 3 || *(++ state.ptr) != 'r' || + *(++ state.ptr) != 'u' || *(++ state.ptr) != 'e') + { + goto e_unknown_value; + } + + if (!new_value (&state, &top, &root, &alloc, json_boolean)) + goto e_alloc_failure; + + top->u.boolean = 1; + + flags |= flag_next; + break; + + case 'f': + + if ((end - state.ptr) <= 4 || *(++ state.ptr) != 'a' || + *(++ state.ptr) != 'l' || *(++ state.ptr) != 's' || + *(++ state.ptr) != 'e') + { + goto e_unknown_value; + } + + if (!new_value (&state, &top, &root, &alloc, json_boolean)) + goto e_alloc_failure; + + flags |= flag_next; + break; + + case 'n': + + if ((end - state.ptr) <= 3 || *(++ state.ptr) != 'u' || + *(++ state.ptr) != 'l' || *(++ state.ptr) != 'l') + { + goto e_unknown_value; + } + + if (!new_value (&state, &top, &root, &alloc, json_null)) + goto e_alloc_failure; + + flags |= flag_next; + break; + + default: + + if (isdigit ((unsigned char) b) || b == '-') + { + if (!new_value (&state, &top, &root, &alloc, json_integer)) + goto e_alloc_failure; + + if (!state.first_pass) + { + while (isdigit ((unsigned char) b) || b == '+' || b == '-' + || b == 'e' || b == 'E' || b == '.') + { + if ( (++ state.ptr) == end) + { + b = 0; + break; + } + + b = *state.ptr; + } + + flags |= flag_next | flag_reproc; + break; + } + + flags &= ~ (flag_num_negative | flag_num_e | + flag_num_e_got_sign | flag_num_e_negative | + flag_num_zero); + + num_digits = 0; + num_fraction = 0; + num_e = 0; + + if (b != '-') + { + flags |= flag_reproc; + break; + } + + flags |= flag_num_negative; + continue; + } + else + { sprintf (error, "%u:%u: Unexpected `%c` when seeking value", line_and_col, b); + goto e_failed; + } + }; + }; + } + else + { + switch (top->type) + { + case json_object: + + switch (b) + { + whitespace: + continue; + + case '"': + + if (flags & flag_need_comma) + { sprintf (error, "%u:%u: Expected `,` before `\"`", line_and_col); + goto e_failed; + } + + flags |= flag_string; + + string = (json_char *) top->_reserved.object_mem; + string_length = 0; + + break; + + case '}': + + flags = (flags & ~ flag_need_comma) | flag_next; + break; + + case ',': + + if (flags & flag_need_comma) + { + flags &= ~ flag_need_comma; + break; + } /* FALLTHRU */ + + default: + sprintf (error, "%u:%u: Unexpected `%c` in object", line_and_col, b); + goto e_failed; + }; + + break; + + case json_integer: + case json_double: + + if (isdigit ((unsigned char)b)) + { + ++ num_digits; + + if (top->type == json_integer || flags & flag_num_e) + { + if (! (flags & flag_num_e)) + { + if (flags & flag_num_zero) + { sprintf (error, "%u:%u: Unexpected `0` before `%c`", line_and_col, b); + goto e_failed; + } + + if (num_digits == 1 && b == '0') + flags |= flag_num_zero; + } + else + { + flags |= flag_num_e_got_sign; + num_e = (num_e * 10) + (b - '0'); + continue; + } + + if (would_overflow(top->u.integer, b)) + { + json_int_t integer = top->u.integer; + -- num_digits; + -- state.ptr; + top->type = json_double; + top->u.dbl = (double)integer; + continue; + } + + top->u.integer = (top->u.integer * 10) + (b - '0'); + continue; + } + + if (flags & flag_num_got_decimal) + num_fraction = (num_fraction * 10) + (b - '0'); + else + top->u.dbl = (top->u.dbl * 10) + (b - '0'); + + continue; + } + + if (b == '+' || b == '-') + { + if ( (flags & flag_num_e) && !(flags & flag_num_e_got_sign)) + { + flags |= flag_num_e_got_sign; + + if (b == '-') + flags |= flag_num_e_negative; + + continue; + } + } + else if (b == '.' && top->type == json_integer) + { + json_int_t integer = top->u.integer; + + if (!num_digits) + { sprintf (error, "%u:%u: Expected digit before `.`", line_and_col); + goto e_failed; + } + + top->type = json_double; + top->u.dbl = (double) integer; + + flags |= flag_num_got_decimal; + num_digits = 0; + continue; + } + + if (! (flags & flag_num_e)) + { + if (top->type == json_double) + { + if (!num_digits) + { sprintf (error, "%u:%u: Expected digit after `.`", line_and_col); + goto e_failed; + } + + top->u.dbl += num_fraction / pow (10.0, num_digits); + } + + if (b == 'e' || b == 'E') + { + flags |= flag_num_e; + + if (top->type == json_integer) + { + json_int_t integer = top->u.integer; + top->type = json_double; + top->u.dbl = (double) integer; + } + + num_digits = 0; + flags &= ~ flag_num_zero; + + continue; + } + } + else + { + if (!num_digits) + { sprintf (error, "%u:%u: Expected digit after `e`", line_and_col); + goto e_failed; + } + + top->u.dbl *= pow (10.0, (flags & flag_num_e_negative ? - num_e : num_e)); + } + + if (flags & flag_num_negative) + { + if (top->type == json_integer) + top->u.integer = - top->u.integer; + else + top->u.dbl = - top->u.dbl; + } + + flags |= flag_next | flag_reproc; + break; + + default: + break; + }; + } + + if (flags & flag_reproc) + { + flags &= ~ flag_reproc; + -- state.ptr; + } + + if (flags & flag_next) + { + flags = (flags & ~ flag_next) | flag_need_comma; + + if (!top->parent) + { + /* root value done */ + + flags |= flag_done; + continue; + } + + if (top->parent->type == json_array) + flags |= flag_seek_value; + + if (!state.first_pass) + { + json_value * parent = top->parent; + + switch (parent->type) + { + case json_object: + + parent->u.object.values + [parent->u.object.length].value = top; + + break; + + case json_array: + + parent->u.array.values + [parent->u.array.length] = top; + + break; + + default: + break; + }; + } + + if ( (++ top->parent->u.array.length) > UINT_MAX - 8) + goto e_overflow; + + top = top->parent; + + continue; + } + } + + alloc = root; + } + + return root; + +e_unknown_value: + + sprintf (error, "%u:%u: Unknown value", line_and_col); + goto e_failed; + +e_alloc_failure: + + strcpy (error, "Memory allocation failure"); + goto e_failed; + +e_overflow: + + sprintf (error, "%u:%u: Too long (caught overflow)", line_and_col); + goto e_failed; + +e_failed: + + if (error_buf) + { + if (*error) + strcpy (error_buf, error); + else + strcpy (error_buf, "Unknown error"); + } + + if (state.first_pass) + alloc = root; + + while (alloc) + { + top = alloc->_reserved.next_alloc; + state.settings.mem_free (alloc, state.settings.user_data); + alloc = top; + } + + if (!state.first_pass) + json_value_free_ex (&state.settings, root); + + return 0; +} + +json_value * json_parse (const json_char * json, size_t length) +{ + json_settings settings = { 0 }; + return json_parse_ex (&settings, json, length, 0); +} + +void json_value_free_ex (json_settings * settings, json_value * value) +{ + json_value * cur_value; + + if (!value) + return; + + value->parent = 0; + + while (value) + { + switch (value->type) + { + case json_array: + + if (!value->u.array.length) + { + settings->mem_free (value->u.array.values, settings->user_data); + break; + } + + value = value->u.array.values [-- value->u.array.length]; + continue; + + case json_object: + + if (!value->u.object.length) + { + settings->mem_free (value->u.object.values, settings->user_data); + break; + } + + value = value->u.object.values [-- value->u.object.length].value; + continue; + + case json_string: + + settings->mem_free (value->u.string.ptr, settings->user_data); + break; + + default: + break; + }; + + cur_value = value; + value = value->parent; + settings->mem_free (cur_value, settings->user_data); + } +} + +void json_value_free (json_value * value) +{ + json_settings settings = { 0 }; + settings.mem_free = default_free; + json_value_free_ex (&settings, value); +} diff --git a/src/json.h b/src/json.h new file mode 100644 index 0000000..1f64316 --- /dev/null +++ b/src/json.h @@ -0,0 +1,288 @@ + +/* vim: set et ts=3 sw=3 sts=3 ft=c: + * + * Copyright (C) 2012-2021 the json-parser authors All rights reserved. + * https://github.com/json-parser/json-parser + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 THE AUTHOR OR CONTRIBUTORS 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. + */ + +#ifndef _JSON_H +#define _JSON_H + +#ifndef json_char + #define json_char char +#endif + +#ifndef json_int_t + #undef JSON_INT_T_OVERRIDDEN + #if defined(_MSC_VER) + #define json_int_t __int64 + #elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || (defined(__cplusplus) && __cplusplus >= 201103L) + /* C99 and C++11 */ + #include + #define json_int_t int_fast64_t + #else + /* C89 */ + #define json_int_t long + #endif +#else + #define JSON_INT_T_OVERRIDDEN 1 +#endif + +#include + +#ifdef __cplusplus + + #include + + extern "C" + { + +#endif + +typedef struct +{ + unsigned long max_memory; /* should be size_t, but would modify the API */ + int settings; + + /* Custom allocator support (leave null to use malloc/free) + */ + + void * (* mem_alloc) (size_t, int zero, void * user_data); + void (* mem_free) (void *, void * user_data); + + void * user_data; /* will be passed to mem_alloc and mem_free */ + + size_t value_extra; /* how much extra space to allocate for values? */ + +} json_settings; + +#define json_enable_comments 0x01 + +typedef enum +{ + json_none, + json_object, + json_array, + json_integer, + json_double, + json_string, + json_boolean, + json_null + +} json_type; + +extern const struct _json_value json_value_none; + +typedef struct _json_object_entry +{ + json_char * name; + unsigned int name_length; + + struct _json_value * value; + +} json_object_entry; + +typedef struct _json_value +{ + struct _json_value * parent; + + json_type type; + + union + { + int boolean; + json_int_t integer; + double dbl; + + struct + { + unsigned int length; + json_char * ptr; /* null terminated */ + + } string; + + struct + { + unsigned int length; + + json_object_entry * values; + + #if defined(__cplusplus) + json_object_entry * begin () const + { return values; + } + json_object_entry * end () const + { return values + length; + } + #endif + + } object; + + struct + { + unsigned int length; + struct _json_value ** values; + + #if defined(__cplusplus) + _json_value ** begin () const + { return values; + } + _json_value ** end () const + { return values + length; + } + #endif + + } array; + + } u; + + union + { + struct _json_value * next_alloc; + void * object_mem; + + } _reserved; + + #ifdef JSON_TRACK_SOURCE + + /* Location of the value in the source JSON + */ + unsigned int line, col; + + #endif + + + /* Some C++ operator sugar */ + + #ifdef __cplusplus + + public: + + inline _json_value () + { memset (this, 0, sizeof (_json_value)); + } + + inline const struct _json_value &operator [] (int index) const + { + if (type != json_array || index < 0 + || ((unsigned int) index) >= u.array.length) + { + return json_value_none; + } + + return *u.array.values [index]; + } + + inline const struct _json_value &operator [] (const char * index) const + { + if (type != json_object) + return json_value_none; + + for (unsigned int i = 0; i < u.object.length; ++ i) + if (!strcmp (u.object.values [i].name, index)) + return *u.object.values [i].value; + + return json_value_none; + } + + inline operator const char * () const + { + switch (type) + { + case json_string: + return u.string.ptr; + + default: + return ""; + }; + } + + inline operator json_int_t () const + { + switch (type) + { + case json_integer: + return u.integer; + + case json_double: + return (json_int_t) u.dbl; + + default: + return 0; + }; + } + + inline operator bool () const + { + if (type != json_boolean) + return false; + + return u.boolean != 0; + } + + inline operator double () const + { + switch (type) + { + case json_integer: + return (double) u.integer; + + case json_double: + return u.dbl; + + default: + return 0; + }; + } + + #endif + +} json_value; + +json_value * json_parse (const json_char * json, + size_t length); + +#define json_error_max 128 +json_value * json_parse_ex (json_settings * settings, + const json_char * json, + size_t length, + char * error); + +void json_value_free (json_value *); + + +/* Not usually necessary, unless you used a custom mem_alloc and now want to + * use a custom mem_free. + */ +void json_value_free_ex (json_settings * settings, + json_value *); + + +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#endif diff --git a/src/sdmon.c b/src/sdmon.c index 62b843d..fbc32ca 100644 --- a/src/sdmon.c +++ b/src/sdmon.c @@ -14,6 +14,10 @@ #include #include #include +#include +#include + +#include "json-builder.h" // this include defines MMC_BLOCK_MAJOR the magic number for the calculation of // MMC_IOC_CMD @@ -68,8 +72,6 @@ #define SD_GEN_CMD 56 #define SD_BLOCK_SIZE 512 -#define formatBool(b) ((b) ? "true" : "false") - int CMD56_data_in(int fd, int cmd56_arg, unsigned char *lba_block_data) { int ret = 0; struct mmc_ioc_cmd idata; @@ -158,7 +160,7 @@ void dump_data_block(char *lba_block_data) { int bytes_to_int(unsigned char byte1, unsigned char byte2, unsigned char byte3, unsigned char byte4) { return (byte1 << 24) | (byte2 << 16) | (byte3 << 8) | byte4; } // convert Little Endian words to int -int nword_to_int(unsigned char *data, int offset, int size) { +json_int_t nword_to_int(unsigned char *data, int offset, int size) { if (size == 4) { return ((data[offset + 3] << 24) | (data[offset + 2] << 16) | (data[offset + 1] << 8) | data[offset]); } else if (size == 8) { @@ -170,7 +172,7 @@ int nword_to_int(unsigned char *data, int offset, int size) { } // convert Big Endian words to int -int nwordbe_to_int(unsigned char *data, int offset, int size) { +json_int_t nwordbe_to_int(unsigned char *data, int offset, int size) { if (size == 4) { return ((data[offset] << 24) | (data[offset + 1] << 16) | (data[offset + 2] << 8) | data[offset + 3]); } else if (size == 8) { @@ -181,6 +183,46 @@ int nwordbe_to_int(unsigned char *data, int offset, int size) { } } +// take a json_value, print it and free it +void json_print_and_free(json_value *j) { + json_serialize_opts jso; + memset(&jso, 0, sizeof(jso)); + jso.indent_size = 2; + jso.mode = json_serialize_mode_multiline; + // jso.opts = json_serialize_opt_no_space_after_comma | json_serialize_opt_no_space_after_colon; + + char *printbuf = malloc(json_measure_ex(j, jso)); + json_serialize_ex(printbuf, j, jso); + puts(printbuf); + free(printbuf); + json_builder_free(j); +} + +// helper to build a formatted string and its json_value +// we first declare the func to tell the compiler that it +// should check the params as for printf +json_value *json_sprintf_new(const char *fmt, ...) __attribute__ ((format (printf, 1, 2))); + +// then we define the func, it returns the built json_value* +json_value *json_sprintf_new(const char *fmt, ...) { + va_list args; + char dststr[128]; + va_start(args, fmt); + vsnprintf(dststr, 128, fmt, args); + va_end(args); + return json_string_new(dststr); +} + +// helper to build a json array of formatted strings +// representing an array of values, return the +// corresponding json_value* we've built +json_value *json_array_build(const char *fmt, const unsigned char *data, size_t offset, size_t size) { + json_value *arr = json_array_new(size); + for (size_t i = 0; i < size; i++) + json_array_push(arr, json_sprintf_new(fmt, data[offset + i])); + return arr; +} + int main(int argc, const char *argv[]) { int fd; const char *device; @@ -198,33 +240,33 @@ int main(int argc, const char *argv[]) { bool addTime = false; const char *option; - // json output - printf("{\n"); - printf("\"version\": \"%s\",\n", VERSION); - - printf("\"date\": \"%d-%02d-%02dT%02d:%02d:%02d.000\",\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); + json_value *j = json_object_new(0); + json_object_push(j, "version", json_string_new(VERSION)); + json_object_push(j, "date", json_sprintf_new("%d-%02d-%02dT%02d:%02d:%02d.000", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec)); // this is more or less static... // "MMC_IOC_CMD": "c048b300", => 0xc048b300 // printf("\"MMC_IOC_CMD\": \"%lx\",\n", MMC_IOC_CMD); if (argc < 2 || argc > 3) { - printf("\"usage\":\"wrong argument count, sdmon [-a]\"\n}\n"); + json_object_push(j, "usage", json_string_new("wrong argument count, sdmon [-a]")); + json_print_and_free(j); exit(1); } device = argv[1]; - printf("\"device\":\"%s\",\n", device); + json_object_push(j, "device", json_string_new(device)); if (argc > 2) { option = argv[2]; if (strcmp(option, "-a") == 0) { addTime = true; } else { - printf("\"usage\":\"wrong option, sdmon [-a]\"\n}\n"); + json_object_push(j, "usage", json_string_new("wrong option, sdmon [-a]")); + json_print_and_free(j); exit(1); } } - printf("\"addTime\": \"%s\",\n", formatBool(addTime)); + json_object_push(j, "addTime", json_boolean_new(addTime ? 1 : 0)); /* * With FreeBSD, make sure kern.geom.debugflags sysctl @@ -234,7 +276,8 @@ int main(int argc, const char *argv[]) { */ fd = open(device, O_RDWR); if (fd < 0) { - printf("\"error\":\"device open failed\"\n}\n"); + json_object_push(j, "error", json_sprintf_new("device open failed (%s)", strerror(errno))); + json_print_and_free(j); exit(1); } @@ -244,30 +287,31 @@ int main(int argc, const char *argv[]) { ret = CMD56_data_in(fd, cmd56_arg, data_in); // we assume success when the call was successful AND the signature is not 0xff 0xff if (ret == 0 && !((data_in[0] == 0xff && data_in[1] == 0xff) || (data_in[0] == 0x00 && data_in[1] == 0x00))) { - printf("\"signature\":\"0x%x 0x%x\",\n", data_in[0], data_in[1]); + json_object_push(j, "signature", json_sprintf_new("0x%x 0x%x", data_in[0], data_in[1])); if (data_in[0] == 0x70 && data_in[1] == 0x58) { - printf("\"Longsys\":\"true\",\n"); - printf("\"SMARTVersions\": %d,\n", nword_to_int(data_in, 4, 4)); - printf("\"sizeOfDevSMART\": %d,\n", nword_to_int(data_in, 12, 4)); - printf("\"originalBadBlock\": %d,\n", nword_to_int(data_in, 16, 4)); - printf("\"increaseBadBlock\": %d,\n", nword_to_int(data_in, 20, 4)); - printf("\"writeAllSectNum\": %d Sector(512Byte),\n", nword_to_int(data_in, 24, 8)); - printf("\"replaceBlockLeft\": %d,\n", nword_to_int(data_in, 32, 4)); - printf("\"degreOfWear\": %.02f Cycle,\n", (float)nword_to_int(data_in, 36, 4) / 1000); - printf("\"sectorTotal\": %d,\n", nword_to_int(data_in, 40, 4)); - printf("\"remainLifeTime\": %d%%,\n", nword_to_int(data_in, 44, 4)); - printf("\"remainWrGBNum\": %.02fTB,\n", (float)nword_to_int(data_in, 48, 4) / 1024); - printf("\"lifeTimeTotal\": %.02f Cycle,\n", (float)nword_to_int(data_in, 52, 4)); - printf("\"phyWrGBNum\": %.02fTB,\n", (float)nword_to_int(data_in, 56, 4) / 1024); + json_object_push(j, "Longsys", json_boolean_new(1)); + json_object_push(j, "SMARTVersions", json_integer_new(nword_to_int(data_in, 4, 4))); + json_object_push(j, "sizeOfDevSMART", json_integer_new(nword_to_int(data_in, 12, 4))); + json_object_push(j, "originalBadBlock", json_integer_new(nword_to_int(data_in, 16, 4))); + json_object_push(j, "increaseBadBlock", json_integer_new(nword_to_int(data_in, 20, 4))); + json_object_push(j, "writeAllSectNum", json_integer_new(nword_to_int(data_in, 24, 8))); + json_object_push(j, "replaceBlockLeft", json_integer_new(nword_to_int(data_in, 32, 4))); + json_object_push(j, "degreeOfWear", json_sprintf_new("%.02f Cycle", (float)nword_to_int(data_in, 36, 4) / 1000.0)); + json_object_push(j, "sectorTotal", json_integer_new(nword_to_int(data_in, 40, 4))); + json_object_push(j, "remainLifeTime", json_sprintf_new("%" PRId64 "%%", nword_to_int(data_in, 44, 4))); + json_object_push(j, "remainWrGBNum", json_sprintf_new("%.02fTB", (float)nword_to_int(data_in, 48, 4) / 1024)); + json_object_push(j, "lifeTimeTotal", json_sprintf_new("%.02f Cycle", (float)nword_to_int(data_in, 52, 4))); + json_object_push(j, "phyWrGBNum", json_sprintf_new("%.02fTB", (float)nword_to_int(data_in, 56, 4) / 1024)); close(fd); - printf("\"success\":true\n}\n"); + json_object_push(j, "success", json_boolean_new(1)); + json_print_and_free(j); exit(0); } if (data_in[0] == 0x44 && (data_in[1] == 0x53 || data_in[1] == 0x57)) { - printf("\"SanDisk\":\"true\",\n"); + json_object_push(j, "SanDisk", json_boolean_new(1)); } /* @@ -281,16 +325,16 @@ int main(int argc, const char *argv[]) { strncpy(tmpstr, (char *)&data_in[2], 6); tmpstr[6] = 0; - printf("\"manufactureYYMMDD\": \"%s\",\n", tmpstr); - printf("\"healthStatusPercentUsed\": %d,\n", data_in[8]); - printf("\"featureRevision\": \"0x%x\",\n", data_in[11]); - printf("\"generationIdentifier\": %d,\n", data_in[14]); + json_object_push(j, "manufactureYYMMDD", json_string_new(tmpstr)); + json_object_push(j, "healthStatusPercentUsed", json_integer_new(data_in[8])); + json_object_push(j, "featureRevision", json_sprintf_new("0x%x", data_in[11])); + json_object_push(j, "generationIdentifier", json_integer_new(data_in[14])); strncpy(tmpstr, (char *)&data_in[49], 32); tmpstr[32] = 0; - printf("\"productString\": \"%s\",\n", tmpstr); - printf("\"powerOnTimes\": %d,\n", bytes_to_int(0, data_in[24], data_in[25], data_in[26])); + json_object_push(j, "productString", json_string_new(tmpstr)); + json_object_push(j, "powerOnTimes", json_integer_new(bytes_to_int(0, data_in[24], data_in[25], data_in[26]))); close(fd); - printf("\"success\":true\n}\n"); + json_object_push(j, "success", json_boolean_new(1)); exit(0); } @@ -300,37 +344,37 @@ int main(int argc, const char *argv[]) { ret = CMD56_data_in(fd, cmd56_arg, data_in); // we assume success when the call was successful AND the signature is not 0xff 0xff if (ret == 0 && !((data_in[0] == 0xff && data_in[1] == 0xff) || (data_in[0] == 0x00 && data_in[1] == 0x00))) { - printf("\"signature\":\"0x%x 0x%x\",\n", data_in[0], data_in[1]); + json_object_push(j, "signature", json_sprintf_new("0x%x 0x%x", data_in[0], data_in[1])); if (data_in[0] == 0x09 && data_in[1] == 0x41) { - printf("\"Adata\":\"true\",\n"); - printf("\"Factory bad block cnt\": %d,\n", (int)((data_in[24] << 8) + data_in[25])); - printf("\"Grown bad block cnt\": %d,\n", (int)(data_in[26])); - printf("\"Spare SLC block cnt\": %d,\n", (int)(data_in[27])); - printf("\"Spare block cnt\": %d,\n", (int)((data_in[30] << 8) + data_in[31])); - printf("\"Data area minimum erase cnt\": %ld,\n", (long)((data_in[32] << 24) + (data_in[33] << 16) + (data_in[34] << 8) + data_in[35])); - printf("\"Data area maximum erase cnt\": %ld,\n", (long)((data_in[36] << 24) + (data_in[37] << 16) + (data_in[38] << 8) + data_in[39])); - printf("\"Data area total erase cnt\": %ld,\n", (long)((data_in[40] << 24) + (data_in[41] << 16) + (data_in[42] << 8) + data_in[43])); - printf("\"Data area average erase cnt\": %ld,\n", (long)((data_in[44] << 24) + (data_in[45] << 16) + (data_in[46] << 8) + data_in[47])); - printf("\"System area minimum erase cnt\": %ld,\n", (long)((data_in[48] << 24) + (data_in[49] << 16) + (data_in[50] << 8) + data_in[51])); - printf("\"System area maximum erase cnt\": %ld,\n", (long)((data_in[52] << 24) + (data_in[53] << 16) + (data_in[54] << 8) + data_in[55])); - printf("\"System area total erase count\": %ld,\n", (long)((data_in[56] << 24) + (data_in[57] << 16) + (data_in[58] << 8) + data_in[59])); - printf("\"System area average erase cnt\": %ld,\n", (long)((data_in[60] << 24) + (data_in[61] << 16) + (data_in[62] << 8) + data_in[63])); - printf("\"Raw card capacity\": %ld MB,\n", (long)((data_in[64] << 24) + (data_in[65] << 16) + (data_in[66] << 8) + data_in[67])); - printf("\"PE Cycle life\": %ld,\n", (long)((data_in[68] << 8) + data_in[69])); - printf("\"Remaining life\": %d%%,\n", (int)data_in[70]); - printf("\"Power cucle cnt\": %ld,\n", (long)((data_in[76] << 24) + (data_in[77] << 16) + (data_in[78] << 8) + data_in[79])); - printf("\"Flash ID\": 0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,\n", data_in[80], data_in[81], data_in[82], data_in[83], data_in[84], data_in[85], data_in[86]); - printf("\"Controller\": %c%c%c%c%c%c,\n", (char)data_in[88], (char)data_in[89], (char)data_in[90], (char)data_in[91], (char)data_in[92], (char)data_in[93]); - printf("\"TLC read reclaim\": %ld,\n", (long)((data_in[96] << 8) + data_in[97])); - printf("\"SLC read reclaim\": %ld,\n", (long)((data_in[98] << 8) + data_in[99])); - printf("\"Firmware block refresh\": %ld,\n", (long)((data_in[100] << 8) + data_in[101])); - printf("\"TLC read threshold\": %ld,\n", (long)((data_in[104] << 24) + (data_in[105] << 16) + (data_in[106] << 8) + data_in[107])); - printf("\"SLC read threshold\": %ld,\n", (long)((data_in[108] << 24) + (data_in[109] << 16) + (data_in[110] << 8) + data_in[111])); - printf("\"FW version\": %c%c%c%c%c%c,\n", (char)data_in[128], (char)data_in[129], (char)data_in[130], (char)data_in[131], (char)data_in[132], (char)data_in[133]); - printf("\"TLC refresh cnt\": %d,\n", (int)((data_in[136] << 24) + (data_in[137] << 16) + (data_in[138] << 8) + data_in[139])); - printf("\"SLC refresh cnt\": %d,\n", (int)((data_in[140] << 24) + (data_in[141] << 16) + (data_in[143] << 8) + data_in[144])); + json_object_push(j, "Adata", json_boolean_new(1)); + json_object_push(j, "Factory bad block cnt", json_integer_new((int)((data_in[24] << 8) + data_in[25]))); + json_object_push(j, "Grown bad block cnt", json_integer_new((int)(data_in[26]))); + json_object_push(j, "Spare SLC block cnt", json_integer_new((int)(data_in[27]))); + json_object_push(j, "Spare block cnt", json_integer_new((int)((data_in[30] << 8) + data_in[31]))); + json_object_push(j, "Data area minimum erase cnt", json_integer_new((long)((data_in[32] << 24) + (data_in[33] << 16) + (data_in[34] << 8) + data_in[35]))); + json_object_push(j, "Data area maximum erase cnt", json_integer_new((long)((data_in[36] << 24) + (data_in[37] << 16) + (data_in[38] << 8) + data_in[39]))); + json_object_push(j, "Data area total erase cnt", json_integer_new((long)((data_in[40] << 24) + (data_in[41] << 16) + (data_in[42] << 8) + data_in[43]))); + json_object_push(j, "Data area average erase cnt", json_integer_new((long)((data_in[44] << 24) + (data_in[45] << 16) + (data_in[46] << 8) + data_in[47]))); + json_object_push(j, "System area minimum erase cnt", json_integer_new((long)((data_in[48] << 24) + (data_in[49] << 16) + (data_in[50] << 8) + data_in[51]))); + json_object_push(j, "System area maximum erase cnt", json_integer_new((long)((data_in[52] << 24) + (data_in[53] << 16) + (data_in[54] << 8) + data_in[55]))); + json_object_push(j, "System area total erase count", json_integer_new((long)((data_in[56] << 24) + (data_in[57] << 16) + (data_in[58] << 8) + data_in[59]))); + json_object_push(j, "System area average erase cnt", json_integer_new((long)((data_in[60] << 24) + (data_in[61] << 16) + (data_in[62] << 8) + data_in[63]))); + json_object_push(j, "Raw card capacity", json_sprintf_new("%ld MB", (long)((data_in[64] << 24) + (data_in[65] << 16) + (data_in[66] << 8) + data_in[67]))); + json_object_push(j, "PE Cycle life", json_integer_new((long)((data_in[68] << 8) + data_in[69]))); + json_object_push(j, "Remaining life", json_sprintf_new("%d%%", (int)data_in[70])); + json_object_push(j, "Power cycle cnt", json_integer_new((long)((data_in[76] << 24) + (data_in[77] << 16) + (data_in[78] << 8) + data_in[79]))); + json_object_push(j, "Flash ID", json_sprintf_new("0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x", data_in[80], data_in[81], data_in[82], data_in[83], data_in[84], data_in[85], data_in[86])); + json_object_push(j, "Controller", json_sprintf_new("%c%c%c%c%c%c", (char)data_in[88], (char)data_in[89], (char)data_in[90], (char)data_in[91], (char)data_in[92], (char)data_in[93])); + json_object_push(j, "TLC read reclaim", json_integer_new((long)((data_in[96] << 8) + data_in[97]))); + json_object_push(j, "SLC read reclaim", json_integer_new((long)((data_in[98] << 8) + data_in[99]))); + json_object_push(j, "Firmware block refresh", json_integer_new((long)((data_in[100] << 8) + data_in[101]))); + json_object_push(j, "TLC read threshold", json_integer_new((long)((data_in[104] << 24) + (data_in[105] << 16) + (data_in[106] << 8) + data_in[107]))); + json_object_push(j, "SLC read threshold", json_integer_new((long)((data_in[108] << 24) + (data_in[109] << 16) + (data_in[110] << 8) + data_in[111]))); + json_object_push(j, "FW version", json_sprintf_new("%c%c%c%c%c%c", (char)data_in[128], (char)data_in[129], (char)data_in[130], (char)data_in[131], (char)data_in[132], (char)data_in[133])); + json_object_push(j, "TLC refresh cnt", json_integer_new((int)((data_in[136] << 24) + (data_in[137] << 16) + (data_in[138] << 8) + data_in[139]))); + json_object_push(j, "SLC refresh cnt", json_integer_new((int)((data_in[140] << 24) + (data_in[141] << 16) + (data_in[143] << 8) + data_in[144]))); close(fd); - printf("\"success\":true\n}\n"); + json_object_push(j, "success", json_boolean_new(1)); exit(0); } } @@ -341,64 +385,64 @@ int main(int argc, const char *argv[]) { ret = CMD56_data_in(fd, cmd56_arg, data_in); // we assume success when the call was successful AND the signature is not 0xff 0xff if (ret == 0 && !((data_in[0] == 0xff && data_in[1] == 0xff) || (data_in[0] == 0x00 && data_in[1] == 0x00))) { - printf("\"signature\":\"0x%x 0x%x\",\n", data_in[0], data_in[1]); + json_object_push(j, "signature", json_sprintf_new("0x%x 0x%x", data_in[0], data_in[1])); if (data_in[0] == 0x54 && data_in[1] == 0x72) { - printf("\"Transcend\":\"true\",\n"); - printf("\"Secured mode\": %d,\n", (int)(data_in[11])); + json_object_push(j, "Transcend", json_boolean_new(1)); + json_object_push(j, "Secured mode", json_integer_new((int)(data_in[11]))); switch (data_in[16]) { case 0x00: - printf("\"Bus width\": 1 bit\n"); + json_object_push(j, "Bus width", json_string_new("1 bit")); break; case 0x10: - printf("\"Bus width\": 4 bits\n"); + json_object_push(j, "Bus width", json_string_new("4 bits")); break; } switch (data_in[18]) { case 0x00: - printf("\"Speed mode\": Class 0\n"); + json_object_push(j, "Speed mode", json_string_new("Class 0")); break; case 0x01: - printf("\"Speed mode\": Class 2\n"); + json_object_push(j, "Speed mode", json_string_new("Class 2")); break; case 0x02: - printf("\"Speed mode\": Class 4\n"); + json_object_push(j, "Speed mode", json_string_new("Class 4")); break; case 0x03: - printf("\"Speed mode\": Class 6\n"); + json_object_push(j, "Speed mode", json_string_new("Class 6")); break; case 0x04: - printf("\"Speed mode\": Class 10\n"); + json_object_push(j, "Speed mode", json_string_new("Class 10")); break; } switch (data_in[19]) { case 0x00: - printf("\"UHS speed grade\": Less than 10MB/s\n"); + json_object_push(j, "UHS speed grade", json_string_new("Less than 10MB/s")); break; case 0x01: - printf("\"UHS speed grade\": 10MB/s and higher\n"); + json_object_push(j, "UHS speed grade", json_string_new("10MB/s and higher")); break; case 0x03: - printf("\"UHS speed grade\": 30MB/s and higher\n"); + json_object_push(j, "UHS speed grade", json_string_new("30MB/s and higher")); break; } - printf("\"New bad blocks cnt\": 0x%02x,\n", data_in[26]); - printf("\"Runtime spare blocks cnt\": 0x%02x,\n", data_in[27]); - printf("\"Abnormal power loss\": %ld,\n", (long)((data_in[31] << 24) + (data_in[30] << 16) + (data_in[29] << 8) + data_in[28])); - printf("\"Minimum erase cnt\": %ld,\n", (long)((data_in[35] << 24) + (data_in[34] << 16) + (data_in[33] << 8) + data_in[32])); - printf("\"Maximum erase cnt\": %ld,\n", (long)((data_in[39] << 24) + (data_in[38] << 16) + (data_in[37] << 8) + data_in[36])); - printf("\"Total erase cnt\": %ld,\n", (long)((data_in[43]) + (data_in[42]) + (data_in[41]) + data_in[40])); - printf("\"Average erase cnt\": %ld,\n", (long)((data_in[47] << 24) + (data_in[46] << 16) + (data_in[45] << 8) + data_in[44])); - - printf("\"Remaining card life\": %d%%,\n", (int)(data_in[70])); - printf("\"Total write CRC cnt\": %d,\n", bytes_to_int(data_in[72], data_in[73], data_in[74], data_in[75])); - printf("\"Power cycle cnt\": %d,\n", bytes_to_int(0, 0, data_in[76], data_in[77])); - - printf("\"NAND flash ID\": 0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,\n", data_in[80], data_in[81], data_in[82], data_in[83], data_in[84], data_in[85]); - printf("\"IC\": %c%c%c%c%c%c%c%c,\n", data_in[87], data_in[88], data_in[89], data_in[90], data_in[91], data_in[92], data_in[93], data_in[94]); - printf("\"fw version\": %c%c%c%c%c%c,\n", data_in[128], data_in[129], data_in[130], data_in[131], data_in[132], data_in[133]); + json_object_push(j, "New bad blocks cnt", json_sprintf_new("0x%02x", data_in[26])); + json_object_push(j, "Runtime spare blocks cnt", json_sprintf_new("0x%02x", data_in[27])); + json_object_push(j, "Abnormal power loss", json_integer_new((long)((data_in[31] << 24) + (data_in[30] << 16) + (data_in[29] << 8) + data_in[28]))); + json_object_push(j, "Minimum erase cnt", json_integer_new((long)((data_in[35] << 24) + (data_in[34] << 16) + (data_in[33] << 8) + data_in[32]))); + json_object_push(j, "Maximum erase cnt", json_integer_new((long)((data_in[39] << 24) + (data_in[38] << 16) + (data_in[37] << 8) + data_in[36]))); + json_object_push(j, "Total erase cnt", json_integer_new((long)((data_in[43]) + (data_in[42]) + (data_in[41]) + data_in[40]))); + json_object_push(j, "Average erase cnt", json_integer_new((long)((data_in[47] << 24) + (data_in[46] << 16) + (data_in[45] << 8) + data_in[44]))); + + json_object_push(j, "Remaining card life", json_sprintf_new("%d%%", (int)(data_in[70]))); + json_object_push(j, "Total write CRC cnt", json_integer_new(bytes_to_int(data_in[72], data_in[73], data_in[74], data_in[75]))); + json_object_push(j, "Power cycle cnt", json_integer_new(bytes_to_int(0, 0, data_in[76], data_in[77]))); + + json_object_push(j, "NAND flash ID", json_sprintf_new("0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x", data_in[80], data_in[81], data_in[82], data_in[83], data_in[84], data_in[85])); + json_object_push(j, "IC", json_sprintf_new("%c%c%c%c%c%c%c%c", data_in[87], data_in[88], data_in[89], data_in[90], data_in[91], data_in[92], data_in[93], data_in[94])); + json_object_push(j, "fw version", json_sprintf_new("%c%c%c%c%c%c", data_in[128], data_in[129], data_in[130], data_in[131], data_in[132], data_in[133])); close(fd); - printf("\"success\":true\n}\n"); + json_object_push(j, "success", json_boolean_new(1)); exit(0); } } @@ -409,14 +453,14 @@ int main(int argc, const char *argv[]) { ret = CMD56_data_in(fd, cmd56_arg, data_in); // we assume success when the call was successful AND the signature is not 0xff 0xff if (ret == 0 && !((data_in[0] == 0xff && data_in[1] == 0xff) || (data_in[0] == 0x00 && data_in[1] == 0x00))) { - printf("\" signature \":\"0x%x 0x%x\",\n", data_in[0], data_in[1]); + json_object_push(j, "signature", json_sprintf_new("0x%x 0x%x", data_in[0], data_in[1])); if (data_in[0] == 0x4d && data_in[1] == 0x45) { - printf("\"Micron\":\"true\",\n"); - printf("\"Percentange step utilization\": %d,\n", (int)(data_in[7])); - printf("\"TLC area utilization\": %d,\n", (int)(data_in[8])); - printf("\"SLC area utilization\": %d,\n", (int)(data_in[9])); + json_object_push(j, "Micron", json_boolean_new(1)); + json_object_push(j, "Percentange step utilization", json_integer_new((int)(data_in[7]))); + json_object_push(j, "TLC area utilization", json_integer_new((int)(data_in[8]))); + json_object_push(j, "SLC area utilization", json_integer_new((int)(data_in[9]))); close(fd); - printf("\"success\":true\n}\n"); + json_object_push(j, "success", json_boolean_new(1)); exit(0); } } @@ -427,70 +471,70 @@ int main(int argc, const char *argv[]) { ret = CMD56_data_in(fd, cmd56_arg, data_in); // we assume success when the call was successful AND the signature is not 0xff 0xff if (ret == 0 && !((data_in[0] == 0xff && data_in[1] == 0xff) || (data_in[0] == 0x00 && data_in[1] == 0x00))) { - printf("\"signature\":\"0x%x 0x%x\",\n", data_in[0], data_in[1]); + json_object_push(j, "signature", json_sprintf_new("0x%x 0x%x", data_in[0], data_in[1])); if (data_in[0] == 0x53 && data_in[1] == 0x77) { printf("\"Swissbit\":\"true\",\n"); strncpy(tmpstr, (char *)&data_in[32], 16); tmpstr[16] = 0; - printf("\"fwVersion\": \"%s\",\n", tmpstr); - printf("\"User area rated cycles\": %d,\n", nwordbe_to_int(data_in, 48, 4)); - printf("\"User area max cycle cnt\": %d,\n", nwordbe_to_int(data_in, 52, 4)); - printf("\"User area total cycle cnt\": %d,\n", nwordbe_to_int(data_in, 56, 4)); - printf("\"User area average cycle cnt\": %d,\n", nwordbe_to_int(data_in, 60, 4)); - printf("\"System area max cycle cnt\": %d,\n", nwordbe_to_int(data_in, 68, 4)); - printf("\"System area total cycle cnt\": %d,\n", nwordbe_to_int(data_in, 72, 4)); - printf("\"System area average cycle cnt\": %d,\n", nwordbe_to_int(data_in, 76, 4)); - printf("\"Remaining Lifetime Percent\": %d%%,\n", (int)(data_in[80])); + json_object_push(j, "fwVersion", json_string_new(tmpstr)); + json_object_push(j, "User area rated cycles", json_integer_new(nwordbe_to_int(data_in, 48, 4))); + json_object_push(j, "User area max cycle cnt", json_integer_new(nwordbe_to_int(data_in, 52, 4))); + json_object_push(j, "User area total cycle cnt", json_integer_new(nwordbe_to_int(data_in, 56, 4))); + json_object_push(j, "User area average cycle cnt", json_integer_new(nwordbe_to_int(data_in, 60, 4))); + json_object_push(j, "User area max cycle cnt", json_integer_new(nwordbe_to_int(data_in, 68, 4))); + json_object_push(j, "User area total cycle cnt", json_integer_new(nwordbe_to_int(data_in, 72, 4))); + json_object_push(j, "User area average cycle cnt", json_integer_new(nwordbe_to_int(data_in, 76, 4))); + json_object_push(j, "Remaining Lifetime Percent", json_integer_new((int)(data_in[80]))); switch (data_in[86]) { case 0x00: - printf("\"Speed mode\": Default speed\n"); + json_object_push(j, "Speed mode", json_string_new("Default speed")); break; case 0x01: - printf("\"Speed mode\": High speed\n"); + json_object_push(j, "Speed mode", json_string_new("High speed")); break; case 0x10: - printf("\"Speed mode\": SDR12 speed\n"); + json_object_push(j, "Speed mode", json_string_new("SDR12 speed")); break; case 0x11: - printf("\"Speed mode\": SDR25 speed\n"); + json_object_push(j, "Speed mode", json_string_new("SDR25 speed")); break; case 0x12: - printf("\"Speed mode\": SDR50 speed\n"); + json_object_push(j, "Speed mode", json_string_new("SDR50 speed")); break; case 0x14: - printf("\"Speed mode\": DDR50 speed\n"); + json_object_push(j, "Speed mode", json_string_new("DDR50 speed")); break; case 0x18: - printf("\"Speed mode\": SDR104 speed\n"); + json_object_push(j, "Speed mode", json_string_new("SDR104 speed")); break; } switch (data_in[87]) { case 0x00: - printf("\"Bus width\": 1 bit\n"); + json_object_push(j, "Bus width", json_string_new("1 bit")); break; case 0x10: - printf("\"Bus width\": 4 bits\n"); + json_object_push(j, "Bus width", json_string_new("4 bits")); break; } - printf("\"User area spare blocks cnt\": %d,\n", nwordbe_to_int(data_in, 88, 4)); - printf("\"System area spare blocks cnt\": %d,\n", nwordbe_to_int(data_in, 92, 4)); - printf("\"User area runtime bad blocks cnt\": %d,\n", nwordbe_to_int(data_in, 96, 4)); - printf("\"System area runtime bad blocks cnt\": %d,\n", nwordbe_to_int(data_in, 100, 4)); - printf("\"User area refresh cnt\": %d,\n", nwordbe_to_int(data_in, 104, 4)); - printf("\"System area refresh cnt\": %d,\n", nwordbe_to_int(data_in, 108, 4)); - printf("\"Interface crc cnt\": %d,\n", nwordbe_to_int(data_in, 112, 4)); - printf("\"Power cycle cnt\": %d,\n", nwordbe_to_int(data_in, 116, 4)); + json_object_push(j, "User area spare blocks cnt", json_integer_new(nwordbe_to_int(data_in, 88, 4))); + json_object_push(j, "System area spare blocks cnt", json_integer_new(nwordbe_to_int(data_in, 92, 4))); + json_object_push(j, "User area runtime bad blocks cnt", json_integer_new(nwordbe_to_int(data_in, 96, 4))); + json_object_push(j, "System area runtime bad blocks cnt", json_integer_new(nwordbe_to_int(data_in, 100, 4))); + json_object_push(j, "User area refresh cnt", json_integer_new(nwordbe_to_int(data_in, 104, 4))); + json_object_push(j, "System area refresh cnt", json_integer_new(nwordbe_to_int(data_in, 108, 4))); + json_object_push(j, "Interface crc cnt", json_integer_new(nwordbe_to_int(data_in, 112, 4))); + json_object_push(j, "Power cycle cnt", json_integer_new(nwordbe_to_int(data_in, 116, 4))); close(fd); - printf("\"success\":true\n}\n"); + json_object_push(j, "success", json_boolean_new(1)); exit(0); } } if (ret == 0) { - printf("\"read_via_cmd56_arg_1\":\"read successful but signature 0x%x 0x%x\",\n", data_in[0], data_in[1]); + json_object_push(j, "read_via_cmd56_arg_1", json_sprintf_new("read successful but signature 0x%x 0x%x", data_in[0], data_in[1])); } else { - printf("\"read_via_cmd56_arg_1\":\"not implemented: %s\",\n", strerror(errno)); + json_object_push(j, "read_via_cmd56_arg_1", json_sprintf_new("not implemented: %s", strerror(errno))); } verbose1("Trying 2step...\n"); @@ -498,8 +542,7 @@ int main(int argc, const char *argv[]) { cmd56_arg = 0x00000010; // all other are 0 ret = CMD56_write(fd, cmd56_arg); if (ret) { - printf("\"error1\":\"1st CMD56 CALL FAILED: %s\",\n", strerror(errno)); - // printf("\"error\":\"1st CMD56 CALL FAILED: %s\"\n}\n", strerror(errno)); + json_object_push(j, "error1", json_sprintf_new("1st CMD56 CALL FAILED: %s", strerror(errno))); // exit(1); } @@ -511,7 +554,8 @@ int main(int argc, const char *argv[]) { cmd56_arg = 0x00000021; ret = CMD56_data_in(fd, cmd56_arg, data_in); if (ret) { - printf("\"error2\":\"2nd CMD56 CALL FAILED: %s\"\n}\n", strerror(errno)); + json_object_push(j, "error2", json_sprintf_new("2nd CMD56 CALL FAILED: %s", strerror(errno))); + json_print_and_free(j); exit(1); } @@ -531,7 +575,7 @@ int main(int argc, const char *argv[]) { // printf("\"badBlockReplaceMaximum\": [\"0x%02x\",\"0x%02x\"],\n", data_in[16], data_in[17]); // badBlockReplaceMaximum is spareBlockCount - printf("\"spareBlockCount\": %d,\n", (int)((data_in[16] << 8) + data_in[17])); + json_object_push(j, "spareBlockCount", json_integer_new((int)((data_in[16] << 8) + data_in[17]))); // printf("\"badBlockCountPerDie1\": " // "[\"0x%02x\",\"0x%02x\",\"0x%02x\",\"0x%02x\",\"0x%02x\",\"0x%02x\"," @@ -551,29 +595,26 @@ int main(int argc, const char *argv[]) { sum = 0; for (i = 32; i < 64; i++) sum += data_in[i]; - printf("\"initialBadBlockCount\": %ld,\n", sum); + json_object_push(j, "initialBadBlockCount", json_integer_new(sum)); // printf("\"goodBlockRatePercentBytes\": [\"0x%02x\",\"0x%02x\"],\n", data_in[64], data_in[65]); // printf("\"goodBlockRatePercent\": %d,\n", (int)((data_in[64]<<8)+data_in[65])); - printf("\"goodBlockRatePercent\": %2.2f,\n", (float)((float)((int)((data_in[64] << 8) + data_in[65])) / 100)); + json_object_push(j, "goodBlockRatePercent", json_double_new((double)((double)((int)((data_in[64] << 8) + data_in[65])) / 100.0))); - printf("\"totalEraseCount\": %ld,\n", (long)((data_in[80] << 24) + (data_in[81] << 16) + (data_in[82] << 8) + data_in[83])); + json_object_push(j, "totalEraseCount", json_integer_new(nword_to_int(data_in, 80, 4))); // printf("\"enduranceRemainLifePercentBytes\": [\"0x%02x\",\"0x%02x\"],\n", data_in[96], data_in[97]); // printf("\"enduranceRemainLifePercent\": %d,\n", (int)((data_in[96]<<8)+data_in[97])); - printf("\"enduranceRemainLifePercent\": %2.2f,\n", (float)((float)((int)((data_in[96] << 8) + data_in[97])) / 100)); + json_object_push(j, "enduranceRemainLifePercent", json_double_new((double)((double)((int)((data_in[96] << 8) + data_in[97])) / 100.0))); - printf("\"avgEraseCount\": %ld,\n", (long)((data_in[104] << 24) + (data_in[105] << 16) + (data_in[98] << 8) + data_in[99])); - printf("\"minEraseCount\": %ld,\n", (long)((data_in[106] << 24) + (data_in[107] << 16) + (data_in[100] << 8) + data_in[101])); - printf("\"maxEraseCount\": %ld,\n", (long)((data_in[108] << 24) + (data_in[109] << 16) + (data_in[102] << 8) + data_in[103])); + json_object_push(j, "avgEraseCount", json_integer_new((long)((data_in[104] << 24) + (data_in[105] << 16) + (data_in[98] << 8) + data_in[99]))); + json_object_push(j, "minEraseCount", json_integer_new((long)((data_in[106] << 24) + (data_in[107] << 16) + (data_in[100] << 8) + data_in[101]))); + json_object_push(j, "maxEraseCount", json_integer_new((long)((data_in[108] << 24) + (data_in[109] << 16) + (data_in[102] << 8) + data_in[103]))); - printf("\"powerUpCount\": %ld,\n", (long)((data_in[112] << 24) + (data_in[113] << 16) + (data_in[114] << 8) + data_in[115])); - printf("\"abnormalPowerOffCount\": %d,\n", (int)((data_in[128] << 8) + data_in[129])); - printf("\"totalRefreshCount\": %d,\n", (int)((data_in[160] << 8) + data_in[161])); - printf("\"productMarker\": " - "[\"0x%02x\",\"0x%02x\",\"0x%02x\",\"0x%02x\",\"0x%02x\",\"0x%02x\"," - "\"0x%02x\",\"0x%02x\"],\n", - data_in[176], data_in[177], data_in[178], data_in[179], data_in[180], data_in[181], data_in[182], data_in[183]); + json_object_push(j, "powerUpCount", json_integer_new(nword_to_int(data_in, 112, 4))); + json_object_push(j, "abnormalPowerOffCount", json_integer_new((int)((data_in[128] << 8) + data_in[129]))); + json_object_push(j, "totalRefreshCount", json_integer_new((int)((data_in[160] << 8) + data_in[161]))); + json_object_push(j, "productMarker", json_array_build("0x%02x", data_in, 176, 8)); // printf("\"badBlockCountPerDie2\": " // "[\"0x%02x\",\"0x%02x\",\"0x%02x\",\"0x%02x\",\"0x%02x\",\"0x%02x\"," // "\"0x%02x\",\"0x%02x\",\"0x%02x\",\"0x%02x\",\"0x%02x\",\"0x%02x\"," @@ -592,10 +633,10 @@ int main(int argc, const char *argv[]) { sum = 0; for (i = 184; i < 216; i++) sum += data_in[i]; - printf("\"laterBadBlockCount\": %ld,\n", sum); + json_object_push(j, "laterBadBlockCount", json_integer_new(sum)); close(fd); - printf("\"success\":true\n}\n"); + json_object_push(j, "success", json_boolean_new(1)); exit(0); }