aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorDouglas Gilbert <dgilbert@interlog.com>2022-04-17 14:15:24 +0000
committerDouglas Gilbert <dgilbert@interlog.com>2022-04-17 14:15:24 +0000
commitf26bb872bf4f043a1b817c808a6947fcb03ecda4 (patch)
tree132ab4d71c41c26fe05e12a57fc9e684ffbd08ec /lib
parent5e7262973c7b0bf753e5e560956c176d9d12424d (diff)
downloadsg3_utils-f26bb872bf4f043a1b817c808a6947fcb03ecda4.tar.gz
sg_lib: add hex2fp(); sg_rtpg: https://github.com/hreinecke/sg3_utils/pull/79 applies; json_builder work
git-svn-id: https://svn.bingwo.ca/repos/sg3_utils/trunk@944 6180dd3e-e324-4e3e-922d-17de1ae2f315
Diffstat (limited to 'lib')
-rw-r--r--lib/Makefile.am4
-rw-r--r--lib/Makefile.in44
-rw-r--r--lib/sg_json_builder.c995
-rw-r--r--lib/sg_json_builder.h324
-rw-r--r--lib/sg_lib.c18
5 files changed, 1364 insertions, 21 deletions
diff --git a/lib/Makefile.am b/lib/Makefile.am
index ec3932ed..b481816d 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -6,7 +6,8 @@ libsgutils2_la_SOURCES = \
sg_cmds_basic2.c \
sg_cmds_extra.c \
sg_cmds_mmc.c \
- sg_pt_common.c
+ sg_pt_common.c \
+ sg_json_builder.c
if OS_LINUX
if PT_DUMMY
@@ -100,4 +101,3 @@ libsgutils2_la_LDFLAGS = -version-info 2:0:0 -no-undefined -release ${PACKAGE_VE
libsgutils2_la_LIBADD = @GETOPT_O_FILES@
libsgutils2_la_DEPENDENCIES = @GETOPT_O_FILES@
-
diff --git a/lib/Makefile.in b/lib/Makefile.in
index 3b07a603..1a6dbeee 100644
--- a/lib/Makefile.in
+++ b/lib/Makefile.in
@@ -146,9 +146,10 @@ am__installdirs = "$(DESTDIR)$(libdir)"
LTLIBRARIES = $(lib_LTLIBRARIES)
am__libsgutils2_la_SOURCES_DIST = sg_lib.c sg_lib_data.c \
sg_lib_names.c sg_cmds_basic.c sg_cmds_basic2.c \
- sg_cmds_extra.c sg_cmds_mmc.c sg_pt_common.c sg_pt_dummy.c \
- sg_pt_linux.c sg_io_linux.c sg_pt_linux_nvme.c sg_pt_win32.c \
- sg_pt_freebsd.c sg_pt_solaris.c sg_pt_osf1.c sg_pt_haiku.c
+ sg_cmds_extra.c sg_cmds_mmc.c sg_pt_common.c sg_json_builder.c \
+ sg_pt_dummy.c sg_pt_linux.c sg_io_linux.c sg_pt_linux_nvme.c \
+ sg_pt_win32.c sg_pt_freebsd.c sg_pt_solaris.c sg_pt_osf1.c \
+ sg_pt_haiku.c
@OS_LINUX_TRUE@@PT_DUMMY_TRUE@am__objects_1 = sg_pt_dummy.lo
@OS_LINUX_TRUE@@PT_DUMMY_FALSE@am__objects_2 = sg_pt_linux.lo \
@OS_LINUX_TRUE@@PT_DUMMY_FALSE@ sg_io_linux.lo \
@@ -166,11 +167,12 @@ am__libsgutils2_la_SOURCES_DIST = sg_lib.c sg_lib_data.c \
@OS_OTHER_TRUE@am__objects_13 = sg_pt_dummy.lo
am_libsgutils2_la_OBJECTS = sg_lib.lo sg_lib_data.lo sg_lib_names.lo \
sg_cmds_basic.lo sg_cmds_basic2.lo sg_cmds_extra.lo \
- sg_cmds_mmc.lo sg_pt_common.lo $(am__objects_1) \
- $(am__objects_2) $(am__objects_3) $(am__objects_4) \
- $(am__objects_5) $(am__objects_6) $(am__objects_7) \
- $(am__objects_8) $(am__objects_9) $(am__objects_10) \
- $(am__objects_11) $(am__objects_12) $(am__objects_13)
+ sg_cmds_mmc.lo sg_pt_common.lo sg_json_builder.lo \
+ $(am__objects_1) $(am__objects_2) $(am__objects_3) \
+ $(am__objects_4) $(am__objects_5) $(am__objects_6) \
+ $(am__objects_7) $(am__objects_8) $(am__objects_9) \
+ $(am__objects_10) $(am__objects_11) $(am__objects_12) \
+ $(am__objects_13)
libsgutils2_la_OBJECTS = $(am_libsgutils2_la_OBJECTS)
AM_V_lt = $(am__v_lt_@AM_V@)
am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
@@ -198,12 +200,13 @@ am__maybe_remake_depfiles = depfiles
am__depfiles_remade = ./$(DEPDIR)/sg_cmds_basic.Plo \
./$(DEPDIR)/sg_cmds_basic2.Plo ./$(DEPDIR)/sg_cmds_extra.Plo \
./$(DEPDIR)/sg_cmds_mmc.Plo ./$(DEPDIR)/sg_io_linux.Plo \
- ./$(DEPDIR)/sg_lib.Plo ./$(DEPDIR)/sg_lib_data.Plo \
- ./$(DEPDIR)/sg_lib_names.Plo ./$(DEPDIR)/sg_pt_common.Plo \
- ./$(DEPDIR)/sg_pt_dummy.Plo ./$(DEPDIR)/sg_pt_freebsd.Plo \
- ./$(DEPDIR)/sg_pt_haiku.Plo ./$(DEPDIR)/sg_pt_linux.Plo \
- ./$(DEPDIR)/sg_pt_linux_nvme.Plo ./$(DEPDIR)/sg_pt_osf1.Plo \
- ./$(DEPDIR)/sg_pt_solaris.Plo ./$(DEPDIR)/sg_pt_win32.Plo
+ ./$(DEPDIR)/sg_json_builder.Plo ./$(DEPDIR)/sg_lib.Plo \
+ ./$(DEPDIR)/sg_lib_data.Plo ./$(DEPDIR)/sg_lib_names.Plo \
+ ./$(DEPDIR)/sg_pt_common.Plo ./$(DEPDIR)/sg_pt_dummy.Plo \
+ ./$(DEPDIR)/sg_pt_freebsd.Plo ./$(DEPDIR)/sg_pt_haiku.Plo \
+ ./$(DEPDIR)/sg_pt_linux.Plo ./$(DEPDIR)/sg_pt_linux_nvme.Plo \
+ ./$(DEPDIR)/sg_pt_osf1.Plo ./$(DEPDIR)/sg_pt_solaris.Plo \
+ ./$(DEPDIR)/sg_pt_win32.Plo
am__mv = mv -f
COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
@@ -376,11 +379,11 @@ top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
libsgutils2_la_SOURCES = sg_lib.c sg_lib_data.c sg_lib_names.c \
sg_cmds_basic.c sg_cmds_basic2.c sg_cmds_extra.c sg_cmds_mmc.c \
- sg_pt_common.c $(am__append_1) $(am__append_2) $(am__append_3) \
- $(am__append_4) $(am__append_5) $(am__append_6) \
- $(am__append_7) $(am__append_8) $(am__append_9) \
- $(am__append_10) $(am__append_11) $(am__append_12) \
- $(am__append_13)
+ sg_pt_common.c sg_json_builder.c $(am__append_1) \
+ $(am__append_2) $(am__append_3) $(am__append_4) \
+ $(am__append_5) $(am__append_6) $(am__append_7) \
+ $(am__append_8) $(am__append_9) $(am__append_10) \
+ $(am__append_11) $(am__append_12) $(am__append_13)
@DEBUG_FALSE@DBG_CFLAGS =
# This is active if --enable-debug given to ./configure
@@ -489,6 +492,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_cmds_extra.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_cmds_mmc.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_io_linux.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_json_builder.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_lib.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_lib_data.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sg_lib_names.Plo@am__quote@ # am--include-marker
@@ -667,6 +671,7 @@ distclean: distclean-am
-rm -f ./$(DEPDIR)/sg_cmds_extra.Plo
-rm -f ./$(DEPDIR)/sg_cmds_mmc.Plo
-rm -f ./$(DEPDIR)/sg_io_linux.Plo
+ -rm -f ./$(DEPDIR)/sg_json_builder.Plo
-rm -f ./$(DEPDIR)/sg_lib.Plo
-rm -f ./$(DEPDIR)/sg_lib_data.Plo
-rm -f ./$(DEPDIR)/sg_lib_names.Plo
@@ -729,6 +734,7 @@ maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/sg_cmds_extra.Plo
-rm -f ./$(DEPDIR)/sg_cmds_mmc.Plo
-rm -f ./$(DEPDIR)/sg_io_linux.Plo
+ -rm -f ./$(DEPDIR)/sg_json_builder.Plo
-rm -f ./$(DEPDIR)/sg_lib.Plo
-rm -f ./$(DEPDIR)/sg_lib_data.Plo
-rm -f ./$(DEPDIR)/sg_lib_names.Plo
diff --git a/lib/sg_json_builder.c b/lib/sg_json_builder.c
new file mode 100644
index 00000000..4d616989
--- /dev/null
+++ b/lib/sg_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 "sg_json_builder.h"
+
+#include <string.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#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/lib/sg_json_builder.h b/lib/sg_json_builder.h
new file mode 100644
index 00000000..9d83b026
--- /dev/null
+++ b/lib/sg_json_builder.h
@@ -0,0 +1,324 @@
+
+/* 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
+
+/*
+ * Used to require json.h from json-parser but what was need as been
+ * included in this header.
+ * https://github.com/udp/json-parser
+ */
+/* #include "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 <stdint.h>
+ #define json_int_t int_fast64_t
+ #else
+ /* C89 */
+ #define json_int_t long
+ #endif
+#else
+ #define JSON_INT_T_OVERRIDDEN 1
+#endif
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+
+ #include <string.h>
+
+ 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
+
+
+ /* C++ operator sugar removed */
+
+} json_value;
+
+#if 0
+#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 *);
+#endif
+
+/* <<< end of code from json-parser's json.h >>> */
+
+
+/* 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/lib/sg_lib.c b/lib/sg_lib.c
index c4fe1d7d..36af6e6f 100644
--- a/lib/sg_lib.c
+++ b/lib/sg_lib.c
@@ -3049,6 +3049,24 @@ hex2str(const uint8_t * b_str, int len, const char * leadin, int format,
return dStrHexStr((const char *)b_str, len, leadin, format, b_len, b);
}
+void
+hex2fp(const uint8_t * b_str, int len, const char * leadin, int format,
+ FILE * fp)
+{
+ int k, num;
+ char b[800]; /* allow for 4 lines of 16 bytes (in hex) each */
+
+ if (leadin && (strlen(leadin) > 118)) {
+ fprintf(fp, ">>> leadin parameter is too large\n");
+ return;
+ }
+ for (k = 0; k < len; k += num) {
+ num = ((k + 64) < len) ? 64 : (len - k);
+ hex2str(b_str + k, num, leadin, format, sizeof(b), b);
+ fprintf(fp, "%s", b);
+ }
+}
+
/* Returns true when executed on big endian machine; else returns false.
* Useful for displaying ATA identify words (which need swapping on a
* big endian machine). */