aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2021-03-03 12:52:21 +0100
committerdec05eba <dec05eba@protonmail.com>2021-03-03 12:52:21 +0100
commitef49fb417f582ec837cde6b551a0bf0cfafe3d4d (patch)
tree0f1230ff9286496a30bb25291e81c6e0157297b2
parent3d5be6f0623e61ecfb1c8086263a615f6b13f1d7 (diff)
Use cJSON instead of the existing json library
-rwxr-xr-xautomediabin116608 -> 108448 bytes
-rwxr-xr-xbuild.sh4
-rw-r--r--depends/cJSON.c3110
-rw-r--r--depends/cJSON.h293
-rw-r--r--src/html.c66
-rw-r--r--src/html.h4
-rw-r--r--src/json.h3077
-rw-r--r--src/main.c67
-rw-r--r--src/rss.c36
-rw-r--r--src/rss.h4
-rw-r--r--src/rss_html_common.c368
-rw-r--r--src/rss_html_common.h9
-rw-r--r--src/transmission.c118
13 files changed, 3582 insertions, 3574 deletions
diff --git a/automedia b/automedia
index 971ef17..b4afd8a 100755
--- a/automedia
+++ b/automedia
Binary files differ
diff --git a/build.sh b/build.sh
index 3acb657..fb79f4a 100755
--- a/build.sh
+++ b/build.sh
@@ -1,5 +1,5 @@
#!/bin/sh
-CFLAGS="-O3 -s -flto"
+CFLAGS="-O3 -s -flto -Wall -Wextra -Werror"
[ -z "$RELEASE" ] && CFLAGS="-O0 -g3 -Wall -Wextra -Werror";
-musl-gcc -static src/main.c src/program.c src/alloc.c src/buffer.c src/fileutils.c src/transmission.c src/rss.c src/html.c src/rss_html_common.c src/download.c src/stringutils.c src/episode.c -o automedia $CFLAGS
+musl-gcc -static src/main.c src/program.c src/alloc.c src/buffer.c src/fileutils.c src/transmission.c src/rss.c src/html.c src/rss_html_common.c src/download.c src/stringutils.c src/episode.c depends/cJSON.c -o automedia $CFLAGS
diff --git a/depends/cJSON.c b/depends/cJSON.c
new file mode 100644
index 0000000..030311c
--- /dev/null
+++ b/depends/cJSON.c
@@ -0,0 +1,3110 @@
+/*
+ Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+/* cJSON */
+/* JSON parser in C. */
+
+/* disable warnings about old C89 functions in MSVC */
+#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER)
+#define _CRT_SECURE_NO_DEPRECATE
+#endif
+
+#ifdef __GNUC__
+#pragma GCC visibility push(default)
+#endif
+#if defined(_MSC_VER)
+#pragma warning (push)
+/* disable warning about single line comments in system headers */
+#pragma warning (disable : 4001)
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <ctype.h>
+#include <float.h>
+
+#ifdef ENABLE_LOCALES
+#include <locale.h>
+#endif
+
+#if defined(_MSC_VER)
+#pragma warning (pop)
+#endif
+#ifdef __GNUC__
+#pragma GCC visibility pop
+#endif
+
+#include "cJSON.h"
+
+/* define our own boolean type */
+#ifdef true
+#undef true
+#endif
+#define true ((cJSON_bool)1)
+
+#ifdef false
+#undef false
+#endif
+#define false ((cJSON_bool)0)
+
+/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */
+#ifndef isinf
+#define isinf(d) (isnan((d - d)) && !isnan(d))
+#endif
+#ifndef isnan
+#define isnan(d) (d != d)
+#endif
+
+#ifndef NAN
+#ifdef _WIN32
+#define NAN sqrt(-1.0)
+#else
+#define NAN 0.0/0.0
+#endif
+#endif
+
+typedef struct {
+ const unsigned char *json;
+ size_t position;
+} error;
+static error global_error = { NULL, 0 };
+
+CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void)
+{
+ return (const char*) (global_error.json + global_error.position);
+}
+
+CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item)
+{
+ if (!cJSON_IsString(item))
+ {
+ return NULL;
+ }
+
+ return item->valuestring;
+}
+
+CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item)
+{
+ if (!cJSON_IsNumber(item))
+ {
+ return (double) NAN;
+ }
+
+ return item->valuedouble;
+}
+
+/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */
+#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 14)
+ #error cJSON.h and cJSON.c have different versions. Make sure that both have the same.
+#endif
+
+CJSON_PUBLIC(const char*) cJSON_Version(void)
+{
+ static char version[15];
+ sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH);
+
+ return version;
+}
+
+/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */
+static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2)
+{
+ if ((string1 == NULL) || (string2 == NULL))
+ {
+ return 1;
+ }
+
+ if (string1 == string2)
+ {
+ return 0;
+ }
+
+ for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++)
+ {
+ if (*string1 == '\0')
+ {
+ return 0;
+ }
+ }
+
+ return tolower(*string1) - tolower(*string2);
+}
+
+typedef struct internal_hooks
+{
+ void *(CJSON_CDECL *allocate)(size_t size);
+ void (CJSON_CDECL *deallocate)(void *pointer);
+ void *(CJSON_CDECL *reallocate)(void *pointer, size_t size);
+} internal_hooks;
+
+#if defined(_MSC_VER)
+/* work around MSVC error C2322: '...' address of dllimport '...' is not static */
+static void * CJSON_CDECL internal_malloc(size_t size)
+{
+ return malloc(size);
+}
+static void CJSON_CDECL internal_free(void *pointer)
+{
+ free(pointer);
+}
+static void * CJSON_CDECL internal_realloc(void *pointer, size_t size)
+{
+ return realloc(pointer, size);
+}
+#else
+#define internal_malloc malloc
+#define internal_free free
+#define internal_realloc realloc
+#endif
+
+/* strlen of character literals resolved at compile time */
+#define static_strlen(string_literal) (sizeof(string_literal) - sizeof(""))
+
+static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc };
+
+static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks)
+{
+ size_t length = 0;
+ unsigned char *copy = NULL;
+
+ if (string == NULL)
+ {
+ return NULL;
+ }
+
+ length = strlen((const char*)string) + sizeof("");
+ copy = (unsigned char*)hooks->allocate(length);
+ if (copy == NULL)
+ {
+ return NULL;
+ }
+ memcpy(copy, string, length);
+
+ return copy;
+}
+
+CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks)
+{
+ if (hooks == NULL)
+ {
+ /* Reset hooks */
+ global_hooks.allocate = malloc;
+ global_hooks.deallocate = free;
+ global_hooks.reallocate = realloc;
+ return;
+ }
+
+ global_hooks.allocate = malloc;
+ if (hooks->malloc_fn != NULL)
+ {
+ global_hooks.allocate = hooks->malloc_fn;
+ }
+
+ global_hooks.deallocate = free;
+ if (hooks->free_fn != NULL)
+ {
+ global_hooks.deallocate = hooks->free_fn;
+ }
+
+ /* use realloc only if both free and malloc are used */
+ global_hooks.reallocate = NULL;
+ if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free))
+ {
+ global_hooks.reallocate = realloc;
+ }
+}
+
+/* Internal constructor. */
+static cJSON *cJSON_New_Item(const internal_hooks * const hooks)
+{
+ cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON));
+ if (node)
+ {
+ memset(node, '\0', sizeof(cJSON));
+ }
+
+ return node;
+}
+
+/* Delete a cJSON structure. */
+CJSON_PUBLIC(void) cJSON_Delete(cJSON *item)
+{
+ cJSON *next = NULL;
+ while (item != NULL)
+ {
+ next = item->next;
+ if (!(item->type & cJSON_IsReference) && (item->child != NULL))
+ {
+ cJSON_Delete(item->child);
+ }
+ if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL))
+ {
+ global_hooks.deallocate(item->valuestring);
+ }
+ if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
+ {
+ global_hooks.deallocate(item->string);
+ }
+ global_hooks.deallocate(item);
+ item = next;
+ }
+}
+
+/* get the decimal point character of the current locale */
+static unsigned char get_decimal_point(void)
+{
+#ifdef ENABLE_LOCALES
+ struct lconv *lconv = localeconv();
+ return (unsigned char) lconv->decimal_point[0];
+#else
+ return '.';
+#endif
+}
+
+typedef struct
+{
+ const unsigned char *content;
+ size_t length;
+ size_t offset;
+ size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */
+ internal_hooks hooks;
+} parse_buffer;
+
+/* check if the given size is left to read in a given parse buffer (starting with 1) */
+#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length))
+/* check if the buffer can be accessed at the given index (starting with 0) */
+#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length))
+#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index))
+/* get a pointer to the buffer at the position */
+#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset)
+
+/* Parse the input text to generate a number, and populate the result into item. */
+static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer)
+{
+ double number = 0;
+ unsigned char *after_end = NULL;
+ unsigned char number_c_string[64];
+ unsigned char decimal_point = get_decimal_point();
+ size_t i = 0;
+
+ if ((input_buffer == NULL) || (input_buffer->content == NULL))
+ {
+ return false;
+ }
+
+ /* copy the number into a temporary buffer and replace '.' with the decimal point
+ * of the current locale (for strtod)
+ * This also takes care of '\0' not necessarily being available for marking the end of the input */
+ for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++)
+ {
+ switch (buffer_at_offset(input_buffer)[i])
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '+':
+ case '-':
+ case 'e':
+ case 'E':
+ number_c_string[i] = buffer_at_offset(input_buffer)[i];
+ break;
+
+ case '.':
+ number_c_string[i] = decimal_point;
+ break;
+
+ default:
+ goto loop_end;
+ }
+ }
+loop_end:
+ number_c_string[i] = '\0';
+
+ number = strtod((const char*)number_c_string, (char**)&after_end);
+ if (number_c_string == after_end)
+ {
+ return false; /* parse_error */
+ }
+
+ item->valuedouble = number;
+
+ /* use saturation in case of overflow */
+ if (number >= INT_MAX)
+ {
+ item->valueint = INT_MAX;
+ }
+ else if (number <= (double)INT_MIN)
+ {
+ item->valueint = INT_MIN;
+ }
+ else
+ {
+ item->valueint = (int)number;
+ }
+
+ item->type = cJSON_Number;
+
+ input_buffer->offset += (size_t)(after_end - number_c_string);
+ return true;
+}
+
+/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */
+CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number)
+{
+ if (number >= INT_MAX)
+ {
+ object->valueint = INT_MAX;
+ }
+ else if (number <= (double)INT_MIN)
+ {
+ object->valueint = INT_MIN;
+ }
+ else
+ {
+ object->valueint = (int)number;
+ }
+
+ return object->valuedouble = number;
+}
+
+CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring)
+{
+ char *copy = NULL;
+ /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */
+ if (!(object->type & cJSON_String) || (object->type & cJSON_IsReference))
+ {
+ return NULL;
+ }
+ if (strlen(valuestring) <= strlen(object->valuestring))
+ {
+ strcpy(object->valuestring, valuestring);
+ return object->valuestring;
+ }
+ copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks);
+ if (copy == NULL)
+ {
+ return NULL;
+ }
+ if (object->valuestring != NULL)
+ {
+ cJSON_free(object->valuestring);
+ }
+ object->valuestring = copy;
+
+ return copy;
+}
+
+typedef struct
+{
+ unsigned char *buffer;
+ size_t length;
+ size_t offset;
+ size_t depth; /* current nesting depth (for formatted printing) */
+ cJSON_bool noalloc;
+ cJSON_bool format; /* is this print a formatted print */
+ internal_hooks hooks;
+} printbuffer;
+
+/* realloc printbuffer if necessary to have at least "needed" bytes more */
+static unsigned char* ensure(printbuffer * const p, size_t needed)
+{
+ unsigned char *newbuffer = NULL;
+ size_t newsize = 0;
+
+ if ((p == NULL) || (p->buffer == NULL))
+ {
+ return NULL;
+ }
+
+ if ((p->length > 0) && (p->offset >= p->length))
+ {
+ /* make sure that offset is valid */
+ return NULL;
+ }
+
+ if (needed > INT_MAX)
+ {
+ /* sizes bigger than INT_MAX are currently not supported */
+ return NULL;
+ }
+
+ needed += p->offset + 1;
+ if (needed <= p->length)
+ {
+ return p->buffer + p->offset;
+ }
+
+ if (p->noalloc) {
+ return NULL;
+ }
+
+ /* calculate new buffer size */
+ if (needed > (INT_MAX / 2))
+ {
+ /* overflow of int, use INT_MAX if possible */
+ if (needed <= INT_MAX)
+ {
+ newsize = INT_MAX;
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+ else
+ {
+ newsize = needed * 2;
+ }
+
+ if (p->hooks.reallocate != NULL)
+ {
+ /* reallocate with realloc if available */
+ newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize);
+ if (newbuffer == NULL)
+ {
+ p->hooks.deallocate(p->buffer);
+ p->length = 0;
+ p->buffer = NULL;
+
+ return NULL;
+ }
+ }
+ else
+ {
+ /* otherwise reallocate manually */
+ newbuffer = (unsigned char*)p->hooks.allocate(newsize);
+ if (!newbuffer)
+ {
+ p->hooks.deallocate(p->buffer);
+ p->length = 0;
+ p->buffer = NULL;
+
+ return NULL;
+ }
+
+ memcpy(newbuffer, p->buffer, p->offset + 1);
+ p->hooks.deallocate(p->buffer);
+ }
+ p->length = newsize;
+ p->buffer = newbuffer;
+
+ return newbuffer + p->offset;
+}
+
+/* calculate the new length of the string in a printbuffer and update the offset */
+static void update_offset(printbuffer * const buffer)
+{
+ const unsigned char *buffer_pointer = NULL;
+ if ((buffer == NULL) || (buffer->buffer == NULL))
+ {
+ return;
+ }
+ buffer_pointer = buffer->buffer + buffer->offset;
+
+ buffer->offset += strlen((const char*)buffer_pointer);
+}
+
+/* securely comparison of floating-point variables */
+static cJSON_bool compare_double(double a, double b)
+{
+ double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b);
+ return (fabs(a - b) <= maxVal * DBL_EPSILON);
+}
+
+/* Render the number nicely from the given item into a string. */
+static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer)
+{
+ unsigned char *output_pointer = NULL;
+ double d = item->valuedouble;
+ int length = 0;
+ size_t i = 0;
+ unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */
+ unsigned char decimal_point = get_decimal_point();
+ double test = 0.0;
+
+ if (output_buffer == NULL)
+ {
+ return false;
+ }
+
+ /* This checks for NaN and Infinity */
+ if (isnan(d) || isinf(d))
+ {
+ length = sprintf((char*)number_buffer, "null");
+ }
+ else
+ {
+ /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */
+ length = sprintf((char*)number_buffer, "%1.15g", d);
+
+ /* Check whether the original double can be recovered */
+ if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d))
+ {
+ /* If not, print with 17 decimal places of precision */
+ length = sprintf((char*)number_buffer, "%1.17g", d);
+ }
+ }
+
+ /* sprintf failed or buffer overrun occurred */
+ if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1)))
+ {
+ return false;
+ }
+
+ /* reserve appropriate space in the output */
+ output_pointer = ensure(output_buffer, (size_t)length + sizeof(""));
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+
+ /* copy the printed number to the output and replace locale
+ * dependent decimal point with '.' */
+ for (i = 0; i < ((size_t)length); i++)
+ {
+ if (number_buffer[i] == decimal_point)
+ {
+ output_pointer[i] = '.';
+ continue;
+ }
+
+ output_pointer[i] = number_buffer[i];
+ }
+ output_pointer[i] = '\0';
+
+ output_buffer->offset += (size_t)length;
+
+ return true;
+}
+
+/* parse 4 digit hexadecimal number */
+static unsigned parse_hex4(const unsigned char * const input)
+{
+ unsigned int h = 0;
+ size_t i = 0;
+
+ for (i = 0; i < 4; i++)
+ {
+ /* parse digit */
+ if ((input[i] >= '0') && (input[i] <= '9'))
+ {
+ h += (unsigned int) input[i] - '0';
+ }
+ else if ((input[i] >= 'A') && (input[i] <= 'F'))
+ {
+ h += (unsigned int) 10 + input[i] - 'A';
+ }
+ else if ((input[i] >= 'a') && (input[i] <= 'f'))
+ {
+ h += (unsigned int) 10 + input[i] - 'a';
+ }
+ else /* invalid */
+ {
+ return 0;
+ }
+
+ if (i < 3)
+ {
+ /* shift left to make place for the next nibble */
+ h = h << 4;
+ }
+ }
+
+ return h;
+}
+
+/* converts a UTF-16 literal to UTF-8
+ * A literal can be one or two sequences of the form \uXXXX */
+static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer)
+{
+ long unsigned int codepoint = 0;
+ unsigned int first_code = 0;
+ const unsigned char *first_sequence = input_pointer;
+ unsigned char utf8_length = 0;
+ unsigned char utf8_position = 0;
+ unsigned char sequence_length = 0;
+ unsigned char first_byte_mark = 0;
+
+ if ((input_end - first_sequence) < 6)
+ {
+ /* input ends unexpectedly */
+ goto fail;
+ }
+
+ /* get the first utf16 sequence */
+ first_code = parse_hex4(first_sequence + 2);
+
+ /* check that the code is valid */
+ if (((first_code >= 0xDC00) && (first_code <= 0xDFFF)))
+ {
+ goto fail;
+ }
+
+ /* UTF16 surrogate pair */
+ if ((first_code >= 0xD800) && (first_code <= 0xDBFF))
+ {
+ const unsigned char *second_sequence = first_sequence + 6;
+ unsigned int second_code = 0;
+ sequence_length = 12; /* \uXXXX\uXXXX */
+
+ if ((input_end - second_sequence) < 6)
+ {
+ /* input ends unexpectedly */
+ goto fail;
+ }
+
+ if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u'))
+ {
+ /* missing second half of the surrogate pair */
+ goto fail;
+ }
+
+ /* get the second utf16 sequence */
+ second_code = parse_hex4(second_sequence + 2);
+ /* check that the code is valid */
+ if ((second_code < 0xDC00) || (second_code > 0xDFFF))
+ {
+ /* invalid second half of the surrogate pair */
+ goto fail;
+ }
+
+
+ /* calculate the unicode codepoint from the surrogate pair */
+ codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF));
+ }
+ else
+ {
+ sequence_length = 6; /* \uXXXX */
+ codepoint = first_code;
+ }
+
+ /* encode as UTF-8
+ * takes at maximum 4 bytes to encode:
+ * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
+ if (codepoint < 0x80)
+ {
+ /* normal ascii, encoding 0xxxxxxx */
+ utf8_length = 1;
+ }
+ else if (codepoint < 0x800)
+ {
+ /* two bytes, encoding 110xxxxx 10xxxxxx */
+ utf8_length = 2;
+ first_byte_mark = 0xC0; /* 11000000 */
+ }
+ else if (codepoint < 0x10000)
+ {
+ /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */
+ utf8_length = 3;
+ first_byte_mark = 0xE0; /* 11100000 */
+ }
+ else if (codepoint <= 0x10FFFF)
+ {
+ /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */
+ utf8_length = 4;
+ first_byte_mark = 0xF0; /* 11110000 */
+ }
+ else
+ {
+ /* invalid unicode codepoint */
+ goto fail;
+ }
+
+ /* encode as utf8 */
+ for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--)
+ {
+ /* 10xxxxxx */
+ (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF);
+ codepoint >>= 6;
+ }
+ /* encode first byte */
+ if (utf8_length > 1)
+ {
+ (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF);
+ }
+ else
+ {
+ (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F);
+ }
+
+ *output_pointer += utf8_length;
+
+ return sequence_length;
+
+fail:
+ return 0;
+}
+
+/* Parse the input text into an unescaped cinput, and populate item. */
+static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer)
+{
+ const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1;
+ const unsigned char *input_end = buffer_at_offset(input_buffer) + 1;
+ unsigned char *output_pointer = NULL;
+ unsigned char *output = NULL;
+
+ /* not a string */
+ if (buffer_at_offset(input_buffer)[0] != '\"')
+ {
+ goto fail;
+ }
+
+ {
+ /* calculate approximate size of the output (overestimate) */
+ size_t allocation_length = 0;
+ size_t skipped_bytes = 0;
+ while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"'))
+ {
+ /* is escape sequence */
+ if (input_end[0] == '\\')
+ {
+ if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length)
+ {
+ /* prevent buffer overflow when last input character is a backslash */
+ goto fail;
+ }
+ skipped_bytes++;
+ input_end++;
+ }
+ input_end++;
+ }
+ if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"'))
+ {
+ goto fail; /* string ended unexpectedly */
+ }
+
+ /* This is at most how much we need for the output */
+ allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes;
+ output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof(""));
+ if (output == NULL)
+ {
+ goto fail; /* allocation failure */
+ }
+ }
+
+ output_pointer = output;
+ /* loop through the string literal */
+ while (input_pointer < input_end)
+ {
+ if (*input_pointer != '\\')
+ {
+ *output_pointer++ = *input_pointer++;
+ }
+ /* escape sequence */
+ else
+ {
+ unsigned char sequence_length = 2;
+ if ((input_end - input_pointer) < 1)
+ {
+ goto fail;
+ }
+
+ switch (input_pointer[1])
+ {
+ case 'b':
+ *output_pointer++ = '\b';
+ break;
+ case 'f':
+ *output_pointer++ = '\f';
+ break;
+ case 'n':
+ *output_pointer++ = '\n';
+ break;
+ case 'r':
+ *output_pointer++ = '\r';
+ break;
+ case 't':
+ *output_pointer++ = '\t';
+ break;
+ case '\"':
+ case '\\':
+ case '/':
+ *output_pointer++ = input_pointer[1];
+ break;
+
+ /* UTF-16 literal */
+ case 'u':
+ sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer);
+ if (sequence_length == 0)
+ {
+ /* failed to convert UTF16-literal to UTF-8 */
+ goto fail;
+ }
+ break;
+
+ default:
+ goto fail;
+ }
+ input_pointer += sequence_length;
+ }
+ }
+
+ /* zero terminate the output */
+ *output_pointer = '\0';
+
+ item->type = cJSON_String;
+ item->valuestring = (char*)output;
+
+ input_buffer->offset = (size_t) (input_end - input_buffer->content);
+ input_buffer->offset++;
+
+ return true;
+
+fail:
+ if (output != NULL)
+ {
+ input_buffer->hooks.deallocate(output);
+ }
+
+ if (input_pointer != NULL)
+ {
+ input_buffer->offset = (size_t)(input_pointer - input_buffer->content);
+ }
+
+ return false;
+}
+
+/* Render the cstring provided to an escaped version that can be printed. */
+static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer)
+{
+ const unsigned char *input_pointer = NULL;
+ unsigned char *output = NULL;
+ unsigned char *output_pointer = NULL;
+ size_t output_length = 0;
+ /* numbers of additional characters needed for escaping */
+ size_t escape_characters = 0;
+
+ if (output_buffer == NULL)
+ {
+ return false;
+ }
+
+ /* empty string */
+ if (input == NULL)
+ {
+ output = ensure(output_buffer, sizeof("\"\""));
+ if (output == NULL)
+ {
+ return false;
+ }
+ strcpy((char*)output, "\"\"");
+
+ return true;
+ }
+
+ /* set "flag" to 1 if something needs to be escaped */
+ for (input_pointer = input; *input_pointer; input_pointer++)
+ {
+ switch (*input_pointer)
+ {
+ case '\"':
+ case '\\':
+ case '\b':
+ case '\f':
+ case '\n':
+ case '\r':
+ case '\t':
+ /* one character escape sequence */
+ escape_characters++;
+ break;
+ default:
+ if (*input_pointer < 32)
+ {
+ /* UTF-16 escape sequence uXXXX */
+ escape_characters += 5;
+ }
+ break;
+ }
+ }
+ output_length = (size_t)(input_pointer - input) + escape_characters;
+
+ output = ensure(output_buffer, output_length + sizeof("\"\""));
+ if (output == NULL)
+ {
+ return false;
+ }
+
+ /* no characters have to be escaped */
+ if (escape_characters == 0)
+ {
+ output[0] = '\"';
+ memcpy(output + 1, input, output_length);
+ output[output_length + 1] = '\"';
+ output[output_length + 2] = '\0';
+
+ return true;
+ }
+
+ output[0] = '\"';
+ output_pointer = output + 1;
+ /* copy the string */
+ for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++)
+ {
+ if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\'))
+ {
+ /* normal character, copy */
+ *output_pointer = *input_pointer;
+ }
+ else
+ {
+ /* character needs to be escaped */
+ *output_pointer++ = '\\';
+ switch (*input_pointer)
+ {
+ case '\\':
+ *output_pointer = '\\';
+ break;
+ case '\"':
+ *output_pointer = '\"';
+ break;
+ case '\b':
+ *output_pointer = 'b';
+ break;
+ case '\f':
+ *output_pointer = 'f';
+ break;
+ case '\n':
+ *output_pointer = 'n';
+ break;
+ case '\r':
+ *output_pointer = 'r';
+ break;
+ case '\t':
+ *output_pointer = 't';
+ break;
+ default:
+ /* escape and print as unicode codepoint */
+ sprintf((char*)output_pointer, "u%04x", *input_pointer);
+ output_pointer += 4;
+ break;
+ }
+ }
+ }
+ output[output_length + 1] = '\"';
+ output[output_length + 2] = '\0';
+
+ return true;
+}
+
+/* Invoke print_string_ptr (which is useful) on an item. */
+static cJSON_bool print_string(const cJSON * const item, printbuffer * const p)
+{
+ return print_string_ptr((unsigned char*)item->valuestring, p);
+}
+
+/* Predeclare these prototypes. */
+static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer);
+static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer);
+static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer);
+static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer);
+static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer);
+static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer);
+
+/* Utility to jump whitespace and cr/lf */
+static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer)
+{
+ if ((buffer == NULL) || (buffer->content == NULL))
+ {
+ return NULL;
+ }
+
+ if (cannot_access_at_index(buffer, 0))
+ {
+ return buffer;
+ }
+
+ while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32))
+ {
+ buffer->offset++;
+ }
+
+ if (buffer->offset == buffer->length)
+ {
+ buffer->offset--;
+ }
+
+ return buffer;
+}
+
+/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */
+static parse_buffer *skip_utf8_bom(parse_buffer * const buffer)
+{
+ if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0))
+ {
+ return NULL;
+ }
+
+ if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0))
+ {
+ buffer->offset += 3;
+ }
+
+ return buffer;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)
+{
+ size_t buffer_length;
+
+ if (NULL == value)
+ {
+ return NULL;
+ }
+
+ /* Adding null character size due to require_null_terminated. */
+ buffer_length = strlen(value) + sizeof("");
+
+ return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated);
+}
+
+/* Parse an object - create a new root, and populate. */
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated)
+{
+ parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } };
+ cJSON *item = NULL;
+
+ /* reset error position */
+ global_error.json = NULL;
+ global_error.position = 0;
+
+ if (value == NULL || 0 == buffer_length)
+ {
+ goto fail;
+ }
+
+ buffer.content = (const unsigned char*)value;
+ buffer.length = buffer_length;
+ buffer.offset = 0;
+ buffer.hooks = global_hooks;
+
+ item = cJSON_New_Item(&global_hooks);
+ if (item == NULL) /* memory fail */
+ {
+ goto fail;
+ }
+
+ if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer))))
+ {
+ /* parse failure. ep is set. */
+ goto fail;
+ }
+
+ /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
+ if (require_null_terminated)
+ {
+ buffer_skip_whitespace(&buffer);
+ if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0')
+ {
+ goto fail;
+ }
+ }
+ if (return_parse_end)
+ {
+ *return_parse_end = (const char*)buffer_at_offset(&buffer);
+ }
+
+ return item;
+
+fail:
+ if (item != NULL)
+ {
+ cJSON_Delete(item);
+ }
+
+ if (value != NULL)
+ {
+ error local_error;
+ local_error.json = (const unsigned char*)value;
+ local_error.position = 0;
+
+ if (buffer.offset < buffer.length)
+ {
+ local_error.position = buffer.offset;
+ }
+ else if (buffer.length > 0)
+ {
+ local_error.position = buffer.length - 1;
+ }
+
+ if (return_parse_end != NULL)
+ {
+ *return_parse_end = (const char*)local_error.json + local_error.position;
+ }
+
+ global_error = local_error;
+ }
+
+ return NULL;
+}
+
+/* Default options for cJSON_Parse */
+CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value)
+{
+ return cJSON_ParseWithOpts(value, 0, 0);
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length)
+{
+ return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0);
+}
+
+#define cjson_min(a, b) (((a) < (b)) ? (a) : (b))
+
+static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks)
+{
+ static const size_t default_buffer_size = 256;
+ printbuffer buffer[1];
+ unsigned char *printed = NULL;
+
+ memset(buffer, 0, sizeof(buffer));
+
+ /* create buffer */
+ buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size);
+ buffer->length = default_buffer_size;
+ buffer->format = format;
+ buffer->hooks = *hooks;
+ if (buffer->buffer == NULL)
+ {
+ goto fail;
+ }
+
+ /* print the value */
+ if (!print_value(item, buffer))
+ {
+ goto fail;
+ }
+ update_offset(buffer);
+
+ /* check if reallocate is available */
+ if (hooks->reallocate != NULL)
+ {
+ printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1);
+ if (printed == NULL) {
+ goto fail;
+ }
+ buffer->buffer = NULL;
+ }
+ else /* otherwise copy the JSON over to a new buffer */
+ {
+ printed = (unsigned char*) hooks->allocate(buffer->offset + 1);
+ if (printed == NULL)
+ {
+ goto fail;
+ }
+ memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1));
+ printed[buffer->offset] = '\0'; /* just to be sure */
+
+ /* free the buffer */
+ hooks->deallocate(buffer->buffer);
+ }
+
+ return printed;
+
+fail:
+ if (buffer->buffer != NULL)
+ {
+ hooks->deallocate(buffer->buffer);
+ }
+
+ if (printed != NULL)
+ {
+ hooks->deallocate(printed);
+ }
+
+ return NULL;
+}
+
+/* Render a cJSON item/entity/structure to text. */
+CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item)
+{
+ return (char*)print(item, true, &global_hooks);
+}
+
+CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item)
+{
+ return (char*)print(item, false, &global_hooks);
+}
+
+CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt)
+{
+ printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
+
+ if (prebuffer < 0)
+ {
+ return NULL;
+ }
+
+ p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer);
+ if (!p.buffer)
+ {
+ return NULL;
+ }
+
+ p.length = (size_t)prebuffer;
+ p.offset = 0;
+ p.noalloc = false;
+ p.format = fmt;
+ p.hooks = global_hooks;
+
+ if (!print_value(item, &p))
+ {
+ global_hooks.deallocate(p.buffer);
+ return NULL;
+ }
+
+ return (char*)p.buffer;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format)
+{
+ printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
+
+ if ((length < 0) || (buffer == NULL))
+ {
+ return false;
+ }
+
+ p.buffer = (unsigned char*)buffer;
+ p.length = (size_t)length;
+ p.offset = 0;
+ p.noalloc = true;
+ p.format = format;
+ p.hooks = global_hooks;
+
+ return print_value(item, &p);
+}
+
+/* Parser core - when encountering text, process appropriately. */
+static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer)
+{
+ if ((input_buffer == NULL) || (input_buffer->content == NULL))
+ {
+ return false; /* no input */
+ }
+
+ /* parse the different types of values */
+ /* null */
+ if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0))
+ {
+ item->type = cJSON_NULL;
+ input_buffer->offset += 4;
+ return true;
+ }
+ /* false */
+ if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0))
+ {
+ item->type = cJSON_False;
+ input_buffer->offset += 5;
+ return true;
+ }
+ /* true */
+ if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0))
+ {
+ item->type = cJSON_True;
+ item->valueint = 1;
+ input_buffer->offset += 4;
+ return true;
+ }
+ /* string */
+ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"'))
+ {
+ return parse_string(item, input_buffer);
+ }
+ /* number */
+ if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9'))))
+ {
+ return parse_number(item, input_buffer);
+ }
+ /* array */
+ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '['))
+ {
+ return parse_array(item, input_buffer);
+ }
+ /* object */
+ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{'))
+ {
+ return parse_object(item, input_buffer);
+ }
+
+ return false;
+}
+
+/* Render a value to text. */
+static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer)
+{
+ unsigned char *output = NULL;
+
+ if ((item == NULL) || (output_buffer == NULL))
+ {
+ return false;
+ }
+
+ switch ((item->type) & 0xFF)
+ {
+ case cJSON_NULL:
+ output = ensure(output_buffer, 5);
+ if (output == NULL)
+ {
+ return false;
+ }
+ strcpy((char*)output, "null");
+ return true;
+
+ case cJSON_False:
+ output = ensure(output_buffer, 6);
+ if (output == NULL)
+ {
+ return false;
+ }
+ strcpy((char*)output, "false");
+ return true;
+
+ case cJSON_True:
+ output = ensure(output_buffer, 5);
+ if (output == NULL)
+ {
+ return false;
+ }
+ strcpy((char*)output, "true");
+ return true;
+
+ case cJSON_Number:
+ return print_number(item, output_buffer);
+
+ case cJSON_Raw:
+ {
+ size_t raw_length = 0;
+ if (item->valuestring == NULL)
+ {
+ return false;
+ }
+
+ raw_length = strlen(item->valuestring) + sizeof("");
+ output = ensure(output_buffer, raw_length);
+ if (output == NULL)
+ {
+ return false;
+ }
+ memcpy(output, item->valuestring, raw_length);
+ return true;
+ }
+
+ case cJSON_String:
+ return print_string(item, output_buffer);
+
+ case cJSON_Array:
+ return print_array(item, output_buffer);
+
+ case cJSON_Object:
+ return print_object(item, output_buffer);
+
+ default:
+ return false;
+ }
+}
+
+/* Build an array from input text. */
+static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer)
+{
+ cJSON *head = NULL; /* head of the linked list */
+ cJSON *current_item = NULL;
+
+ if (input_buffer->depth >= CJSON_NESTING_LIMIT)
+ {
+ return false; /* to deeply nested */
+ }
+ input_buffer->depth++;
+
+ if (buffer_at_offset(input_buffer)[0] != '[')
+ {
+ /* not an array */
+ goto fail;
+ }
+
+ input_buffer->offset++;
+ buffer_skip_whitespace(input_buffer);
+ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']'))
+ {
+ /* empty array */
+ goto success;
+ }
+
+ /* check if we skipped to the end of the buffer */
+ if (cannot_access_at_index(input_buffer, 0))
+ {
+ input_buffer->offset--;
+ goto fail;
+ }
+
+ /* step back to character in front of the first element */
+ input_buffer->offset--;
+ /* loop through the comma separated array elements */
+ do
+ {
+ /* allocate next item */
+ cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
+ if (new_item == NULL)
+ {
+ goto fail; /* allocation failure */
+ }
+
+ /* attach next item to list */
+ if (head == NULL)
+ {
+ /* start the linked list */
+ current_item = head = new_item;
+ }
+ else
+ {
+ /* add to the end and advance */
+ current_item->next = new_item;
+ new_item->prev = current_item;
+ current_item = new_item;
+ }
+
+ /* parse next value */
+ input_buffer->offset++;
+ buffer_skip_whitespace(input_buffer);
+ if (!parse_value(current_item, input_buffer))
+ {
+ goto fail; /* failed to parse value */
+ }
+ buffer_skip_whitespace(input_buffer);
+ }
+ while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
+
+ if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']')
+ {
+ goto fail; /* expected end of array */
+ }
+
+success:
+ input_buffer->depth--;
+
+ if (head != NULL) {
+ head->prev = current_item;
+ }
+
+ item->type = cJSON_Array;
+ item->child = head;
+
+ input_buffer->offset++;
+
+ return true;
+
+fail:
+ if (head != NULL)
+ {
+ cJSON_Delete(head);
+ }
+
+ return false;
+}
+
+/* Render an array to text */
+static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer)
+{
+ unsigned char *output_pointer = NULL;
+ size_t length = 0;
+ cJSON *current_element = item->child;
+
+ if (output_buffer == NULL)
+ {
+ return false;
+ }
+
+ /* Compose the output array. */
+ /* opening square bracket */
+ output_pointer = ensure(output_buffer, 1);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+
+ *output_pointer = '[';
+ output_buffer->offset++;
+ output_buffer->depth++;
+
+ while (current_element != NULL)
+ {
+ if (!print_value(current_element, output_buffer))
+ {
+ return false;
+ }
+ update_offset(output_buffer);
+ if (current_element->next)
+ {
+ length = (size_t) (output_buffer->format ? 2 : 1);
+ output_pointer = ensure(output_buffer, length + 1);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+ *output_pointer++ = ',';
+ if(output_buffer->format)
+ {
+ *output_pointer++ = ' ';
+ }
+ *output_pointer = '\0';
+ output_buffer->offset += length;
+ }
+ current_element = current_element->next;
+ }
+
+ output_pointer = ensure(output_buffer, 2);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+ *output_pointer++ = ']';
+ *output_pointer = '\0';
+ output_buffer->depth--;
+
+ return true;
+}
+
+/* Build an object from the text. */
+static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer)
+{
+ cJSON *head = NULL; /* linked list head */
+ cJSON *current_item = NULL;
+
+ if (input_buffer->depth >= CJSON_NESTING_LIMIT)
+ {
+ return false; /* to deeply nested */
+ }
+ input_buffer->depth++;
+
+ if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{'))
+ {
+ goto fail; /* not an object */
+ }
+
+ input_buffer->offset++;
+ buffer_skip_whitespace(input_buffer);
+ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}'))
+ {
+ goto success; /* empty object */
+ }
+
+ /* check if we skipped to the end of the buffer */
+ if (cannot_access_at_index(input_buffer, 0))
+ {
+ input_buffer->offset--;
+ goto fail;
+ }
+
+ /* step back to character in front of the first element */
+ input_buffer->offset--;
+ /* loop through the comma separated array elements */
+ do
+ {
+ /* allocate next item */
+ cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
+ if (new_item == NULL)
+ {
+ goto fail; /* allocation failure */
+ }
+
+ /* attach next item to list */
+ if (head == NULL)
+ {
+ /* start the linked list */
+ current_item = head = new_item;
+ }
+ else
+ {
+ /* add to the end and advance */
+ current_item->next = new_item;
+ new_item->prev = current_item;
+ current_item = new_item;
+ }
+
+ /* parse the name of the child */
+ input_buffer->offset++;
+ buffer_skip_whitespace(input_buffer);
+ if (!parse_string(current_item, input_buffer))
+ {
+ goto fail; /* failed to parse name */
+ }
+ buffer_skip_whitespace(input_buffer);
+
+ /* swap valuestring and string, because we parsed the name */
+ current_item->string = current_item->valuestring;
+ current_item->valuestring = NULL;
+
+ if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':'))
+ {
+ goto fail; /* invalid object */
+ }
+
+ /* parse the value */
+ input_buffer->offset++;
+ buffer_skip_whitespace(input_buffer);
+ if (!parse_value(current_item, input_buffer))
+ {
+ goto fail; /* failed to parse value */
+ }
+ buffer_skip_whitespace(input_buffer);
+ }
+ while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
+
+ if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}'))
+ {
+ goto fail; /* expected end of object */
+ }
+
+success:
+ input_buffer->depth--;
+
+ if (head != NULL) {
+ head->prev = current_item;
+ }
+
+ item->type = cJSON_Object;
+ item->child = head;
+
+ input_buffer->offset++;
+ return true;
+
+fail:
+ if (head != NULL)
+ {
+ cJSON_Delete(head);
+ }
+
+ return false;
+}
+
+/* Render an object to text. */
+static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer)
+{
+ unsigned char *output_pointer = NULL;
+ size_t length = 0;
+ cJSON *current_item = item->child;
+
+ if (output_buffer == NULL)
+ {
+ return false;
+ }
+
+ /* Compose the output: */
+ length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */
+ output_pointer = ensure(output_buffer, length + 1);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+
+ *output_pointer++ = '{';
+ output_buffer->depth++;
+ if (output_buffer->format)
+ {
+ *output_pointer++ = '\n';
+ }
+ output_buffer->offset += length;
+
+ while (current_item)
+ {
+ if (output_buffer->format)
+ {
+ size_t i;
+ output_pointer = ensure(output_buffer, output_buffer->depth);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+ for (i = 0; i < output_buffer->depth; i++)
+ {
+ *output_pointer++ = '\t';
+ }
+ output_buffer->offset += output_buffer->depth;
+ }
+
+ /* print key */
+ if (!print_string_ptr((unsigned char*)current_item->string, output_buffer))
+ {
+ return false;
+ }
+ update_offset(output_buffer);
+
+ length = (size_t) (output_buffer->format ? 2 : 1);
+ output_pointer = ensure(output_buffer, length);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+ *output_pointer++ = ':';
+ if (output_buffer->format)
+ {
+ *output_pointer++ = '\t';
+ }
+ output_buffer->offset += length;
+
+ /* print value */
+ if (!print_value(current_item, output_buffer))
+ {
+ return false;
+ }
+ update_offset(output_buffer);
+
+ /* print comma if not last */
+ length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0));
+ output_pointer = ensure(output_buffer, length + 1);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+ if (current_item->next)
+ {
+ *output_pointer++ = ',';
+ }
+
+ if (output_buffer->format)
+ {
+ *output_pointer++ = '\n';
+ }
+ *output_pointer = '\0';
+ output_buffer->offset += length;
+
+ current_item = current_item->next;
+ }
+
+ output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+ if (output_buffer->format)
+ {
+ size_t i;
+ for (i = 0; i < (output_buffer->depth - 1); i++)
+ {
+ *output_pointer++ = '\t';
+ }
+ }
+ *output_pointer++ = '}';
+ *output_pointer = '\0';
+ output_buffer->depth--;
+
+ return true;
+}
+
+/* Get Array size/item / object item. */
+CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array)
+{
+ cJSON *child = NULL;
+ size_t size = 0;
+
+ if (array == NULL)
+ {
+ return 0;
+ }
+
+ child = array->child;
+
+ while(child != NULL)
+ {
+ size++;
+ child = child->next;
+ }
+
+ /* FIXME: Can overflow here. Cannot be fixed without breaking the API */
+
+ return (int)size;
+}
+
+static cJSON* get_array_item(const cJSON *array, size_t index)
+{
+ cJSON *current_child = NULL;
+
+ if (array == NULL)
+ {
+ return NULL;
+ }
+
+ current_child = array->child;
+ while ((current_child != NULL) && (index > 0))
+ {
+ index--;
+ current_child = current_child->next;
+ }
+
+ return current_child;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index)
+{
+ if (index < 0)
+ {
+ return NULL;
+ }
+
+ return get_array_item(array, (size_t)index);
+}
+
+static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive)
+{
+ cJSON *current_element = NULL;
+
+ if ((object == NULL) || (name == NULL))
+ {
+ return NULL;
+ }
+
+ current_element = object->child;
+ if (case_sensitive)
+ {
+ while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0))
+ {
+ current_element = current_element->next;
+ }
+ }
+ else
+ {
+ while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0))
+ {
+ current_element = current_element->next;
+ }
+ }
+
+ if ((current_element == NULL) || (current_element->string == NULL)) {
+ return NULL;
+ }
+
+ return current_element;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string)
+{
+ return get_object_item(object, string, false);
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string)
+{
+ return get_object_item(object, string, true);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string)
+{
+ return cJSON_GetObjectItem(object, string) ? 1 : 0;
+}
+
+/* Utility for array list handling. */
+static void suffix_object(cJSON *prev, cJSON *item)
+{
+ prev->next = item;
+ item->prev = prev;
+}
+
+/* Utility for handling references. */
+static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks)
+{
+ cJSON *reference = NULL;
+ if (item == NULL)
+ {
+ return NULL;
+ }
+
+ reference = cJSON_New_Item(hooks);
+ if (reference == NULL)
+ {
+ return NULL;
+ }
+
+ memcpy(reference, item, sizeof(cJSON));
+ reference->string = NULL;
+ reference->type |= cJSON_IsReference;
+ reference->next = reference->prev = NULL;
+ return reference;
+}
+
+static cJSON_bool add_item_to_array(cJSON *array, cJSON *item)
+{
+ cJSON *child = NULL;
+
+ if ((item == NULL) || (array == NULL) || (array == item))
+ {
+ return false;
+ }
+
+ child = array->child;
+ /*
+ * To find the last item in array quickly, we use prev in array
+ */
+ if (child == NULL)
+ {
+ /* list is empty, start new one */
+ array->child = item;
+ item->prev = item;
+ item->next = NULL;
+ }
+ else
+ {
+ /* append to the end */
+ if (child->prev)
+ {
+ suffix_object(child->prev, item);
+ array->child->prev = item;
+ }
+ }
+
+ return true;
+}
+
+/* Add item to array/object. */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item)
+{
+ return add_item_to_array(array, item);
+}
+
+#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
+ #pragma GCC diagnostic push
+#endif
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wcast-qual"
+#endif
+/* helper function to cast away const */
+static void* cast_away_const(const void* string)
+{
+ return (void*)string;
+}
+#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
+ #pragma GCC diagnostic pop
+#endif
+
+
+static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key)
+{
+ char *new_key = NULL;
+ int new_type = cJSON_Invalid;
+
+ if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item))
+ {
+ return false;
+ }
+
+ if (constant_key)
+ {
+ new_key = (char*)cast_away_const(string);
+ new_type = item->type | cJSON_StringIsConst;
+ }
+ else
+ {
+ new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks);
+ if (new_key == NULL)
+ {
+ return false;
+ }
+
+ new_type = item->type & ~cJSON_StringIsConst;
+ }
+
+ if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
+ {
+ hooks->deallocate(item->string);
+ }
+
+ item->string = new_key;
+ item->type = new_type;
+
+ return add_item_to_array(object, item);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item)
+{
+ return add_item_to_object(object, string, item, &global_hooks, false);
+}
+
+/* Add an item to an object with constant string as key */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item)
+{
+ return add_item_to_object(object, string, item, &global_hooks, true);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)
+{
+ if (array == NULL)
+ {
+ return false;
+ }
+
+ return add_item_to_array(array, create_reference(item, &global_hooks));
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item)
+{
+ if ((object == NULL) || (string == NULL))
+ {
+ return false;
+ }
+
+ return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false);
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name)
+{
+ cJSON *null = cJSON_CreateNull();
+ if (add_item_to_object(object, name, null, &global_hooks, false))
+ {
+ return null;
+ }
+
+ cJSON_Delete(null);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name)
+{
+ cJSON *true_item = cJSON_CreateTrue();
+ if (add_item_to_object(object, name, true_item, &global_hooks, false))
+ {
+ return true_item;
+ }
+
+ cJSON_Delete(true_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name)
+{
+ cJSON *false_item = cJSON_CreateFalse();
+ if (add_item_to_object(object, name, false_item, &global_hooks, false))
+ {
+ return false_item;
+ }
+
+ cJSON_Delete(false_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean)
+{
+ cJSON *bool_item = cJSON_CreateBool(boolean);
+ if (add_item_to_object(object, name, bool_item, &global_hooks, false))
+ {
+ return bool_item;
+ }
+
+ cJSON_Delete(bool_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number)
+{
+ cJSON *number_item = cJSON_CreateNumber(number);
+ if (add_item_to_object(object, name, number_item, &global_hooks, false))
+ {
+ return number_item;
+ }
+
+ cJSON_Delete(number_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string)
+{
+ cJSON *string_item = cJSON_CreateString(string);
+ if (add_item_to_object(object, name, string_item, &global_hooks, false))
+ {
+ return string_item;
+ }
+
+ cJSON_Delete(string_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw)
+{
+ cJSON *raw_item = cJSON_CreateRaw(raw);
+ if (add_item_to_object(object, name, raw_item, &global_hooks, false))
+ {
+ return raw_item;
+ }
+
+ cJSON_Delete(raw_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name)
+{
+ cJSON *object_item = cJSON_CreateObject();
+ if (add_item_to_object(object, name, object_item, &global_hooks, false))
+ {
+ return object_item;
+ }
+
+ cJSON_Delete(object_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name)
+{
+ cJSON *array = cJSON_CreateArray();
+ if (add_item_to_object(object, name, array, &global_hooks, false))
+ {
+ return array;
+ }
+
+ cJSON_Delete(array);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item)
+{
+ if ((parent == NULL) || (item == NULL))
+ {
+ return NULL;
+ }
+
+ if (item != parent->child)
+ {
+ /* not the first element */
+ item->prev->next = item->next;
+ }
+ if (item->next != NULL)
+ {
+ /* not the last element */
+ item->next->prev = item->prev;
+ }
+
+ if (item == parent->child)
+ {
+ /* first element */
+ parent->child = item->next;
+ }
+ else if (item->next == NULL)
+ {
+ /* last element */
+ parent->child->prev = item->prev;
+ }
+
+ /* make sure the detached item doesn't point anywhere anymore */
+ item->prev = NULL;
+ item->next = NULL;
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which)
+{
+ if (which < 0)
+ {
+ return NULL;
+ }
+
+ return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which));
+}
+
+CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which)
+{
+ cJSON_Delete(cJSON_DetachItemFromArray(array, which));
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string)
+{
+ cJSON *to_detach = cJSON_GetObjectItem(object, string);
+
+ return cJSON_DetachItemViaPointer(object, to_detach);
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string)
+{
+ cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string);
+
+ return cJSON_DetachItemViaPointer(object, to_detach);
+}
+
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string)
+{
+ cJSON_Delete(cJSON_DetachItemFromObject(object, string));
+}
+
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string)
+{
+ cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string));
+}
+
+/* Replace array/object items with new ones. */
+CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem)
+{
+ cJSON *after_inserted = NULL;
+
+ if (which < 0)
+ {
+ return false;
+ }
+
+ after_inserted = get_array_item(array, (size_t)which);
+ if (after_inserted == NULL)
+ {
+ return add_item_to_array(array, newitem);
+ }
+
+ newitem->next = after_inserted;
+ newitem->prev = after_inserted->prev;
+ after_inserted->prev = newitem;
+ if (after_inserted == array->child)
+ {
+ array->child = newitem;
+ }
+ else
+ {
+ newitem->prev->next = newitem;
+ }
+ return true;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement)
+{
+ if ((parent == NULL) || (replacement == NULL) || (item == NULL))
+ {
+ return false;
+ }
+
+ if (replacement == item)
+ {
+ return true;
+ }
+
+ replacement->next = item->next;
+ replacement->prev = item->prev;
+
+ if (replacement->next != NULL)
+ {
+ replacement->next->prev = replacement;
+ }
+ if (parent->child == item)
+ {
+ if (parent->child->prev == parent->child)
+ {
+ replacement->prev = replacement;
+ }
+ parent->child = replacement;
+ }
+ else
+ { /*
+ * To find the last item in array quickly, we use prev in array.
+ * We can't modify the last item's next pointer where this item was the parent's child
+ */
+ if (replacement->prev != NULL)
+ {
+ replacement->prev->next = replacement;
+ }
+ if (replacement->next == NULL)
+ {
+ parent->child->prev = replacement;
+ }
+ }
+
+ item->next = NULL;
+ item->prev = NULL;
+ cJSON_Delete(item);
+
+ return true;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem)
+{
+ if (which < 0)
+ {
+ return false;
+ }
+
+ return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem);
+}
+
+static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive)
+{
+ if ((replacement == NULL) || (string == NULL))
+ {
+ return false;
+ }
+
+ /* replace the name in the replacement */
+ if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL))
+ {
+ cJSON_free(replacement->string);
+ }
+ replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
+ replacement->type &= ~cJSON_StringIsConst;
+
+ return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem)
+{
+ return replace_item_in_object(object, string, newitem, false);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem)
+{
+ return replace_item_in_object(object, string, newitem, true);
+}
+
+/* Create basic types: */
+CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type = cJSON_NULL;
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type = cJSON_True;
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type = cJSON_False;
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type = boolean ? cJSON_True : cJSON_False;
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type = cJSON_Number;
+ item->valuedouble = num;
+
+ /* use saturation in case of overflow */
+ if (num >= INT_MAX)
+ {
+ item->valueint = INT_MAX;
+ }
+ else if (num <= (double)INT_MIN)
+ {
+ item->valueint = INT_MIN;
+ }
+ else
+ {
+ item->valueint = (int)num;
+ }
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type = cJSON_String;
+ item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
+ if(!item->valuestring)
+ {
+ cJSON_Delete(item);
+ return NULL;
+ }
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if (item != NULL)
+ {
+ item->type = cJSON_String | cJSON_IsReference;
+ item->valuestring = (char*)cast_away_const(string);
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if (item != NULL) {
+ item->type = cJSON_Object | cJSON_IsReference;
+ item->child = (cJSON*)cast_away_const(child);
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) {
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if (item != NULL) {
+ item->type = cJSON_Array | cJSON_IsReference;
+ item->child = (cJSON*)cast_away_const(child);
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type = cJSON_Raw;
+ item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks);
+ if(!item->valuestring)
+ {
+ cJSON_Delete(item);
+ return NULL;
+ }
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type=cJSON_Array;
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if (item)
+ {
+ item->type = cJSON_Object;
+ }
+
+ return item;
+}
+
+/* Create Arrays: */
+CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count)
+{
+ size_t i = 0;
+ cJSON *n = NULL;
+ cJSON *p = NULL;
+ cJSON *a = NULL;
+
+ if ((count < 0) || (numbers == NULL))
+ {
+ return NULL;
+ }
+
+ a = cJSON_CreateArray();
+
+ for(i = 0; a && (i < (size_t)count); i++)
+ {
+ n = cJSON_CreateNumber(numbers[i]);
+ if (!n)
+ {
+ cJSON_Delete(a);
+ return NULL;
+ }
+ if(!i)
+ {
+ a->child = n;
+ }
+ else
+ {
+ suffix_object(p, n);
+ }
+ p = n;
+ }
+
+ if (a && a->child) {
+ a->child->prev = n;
+ }
+
+ return a;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count)
+{
+ size_t i = 0;
+ cJSON *n = NULL;
+ cJSON *p = NULL;
+ cJSON *a = NULL;
+
+ if ((count < 0) || (numbers == NULL))
+ {
+ return NULL;
+ }
+
+ a = cJSON_CreateArray();
+
+ for(i = 0; a && (i < (size_t)count); i++)
+ {
+ n = cJSON_CreateNumber((double)numbers[i]);
+ if(!n)
+ {
+ cJSON_Delete(a);
+ return NULL;
+ }
+ if(!i)
+ {
+ a->child = n;
+ }
+ else
+ {
+ suffix_object(p, n);
+ }
+ p = n;
+ }
+
+ if (a && a->child) {
+ a->child->prev = n;
+ }
+
+ return a;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count)
+{
+ size_t i = 0;
+ cJSON *n = NULL;
+ cJSON *p = NULL;
+ cJSON *a = NULL;
+
+ if ((count < 0) || (numbers == NULL))
+ {
+ return NULL;
+ }
+
+ a = cJSON_CreateArray();
+
+ for(i = 0; a && (i < (size_t)count); i++)
+ {
+ n = cJSON_CreateNumber(numbers[i]);
+ if(!n)
+ {
+ cJSON_Delete(a);
+ return NULL;
+ }
+ if(!i)
+ {
+ a->child = n;
+ }
+ else
+ {
+ suffix_object(p, n);
+ }
+ p = n;
+ }
+
+ if (a && a->child) {
+ a->child->prev = n;
+ }
+
+ return a;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count)
+{
+ size_t i = 0;
+ cJSON *n = NULL;
+ cJSON *p = NULL;
+ cJSON *a = NULL;
+
+ if ((count < 0) || (strings == NULL))
+ {
+ return NULL;
+ }
+
+ a = cJSON_CreateArray();
+
+ for (i = 0; a && (i < (size_t)count); i++)
+ {
+ n = cJSON_CreateString(strings[i]);
+ if(!n)
+ {
+ cJSON_Delete(a);
+ return NULL;
+ }
+ if(!i)
+ {
+ a->child = n;
+ }
+ else
+ {
+ suffix_object(p,n);
+ }
+ p = n;
+ }
+
+ if (a && a->child) {
+ a->child->prev = n;
+ }
+
+ return a;
+}
+
+/* Duplication */
+CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse)
+{
+ cJSON *newitem = NULL;
+ cJSON *child = NULL;
+ cJSON *next = NULL;
+ cJSON *newchild = NULL;
+
+ /* Bail on bad ptr */
+ if (!item)
+ {
+ goto fail;
+ }
+ /* Create new item */
+ newitem = cJSON_New_Item(&global_hooks);
+ if (!newitem)
+ {
+ goto fail;
+ }
+ /* Copy over all vars */
+ newitem->type = item->type & (~cJSON_IsReference);
+ newitem->valueint = item->valueint;
+ newitem->valuedouble = item->valuedouble;
+ if (item->valuestring)
+ {
+ newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks);
+ if (!newitem->valuestring)
+ {
+ goto fail;
+ }
+ }
+ if (item->string)
+ {
+ newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks);
+ if (!newitem->string)
+ {
+ goto fail;
+ }
+ }
+ /* If non-recursive, then we're done! */
+ if (!recurse)
+ {
+ return newitem;
+ }
+ /* Walk the ->next chain for the child. */
+ child = item->child;
+ while (child != NULL)
+ {
+ newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */
+ if (!newchild)
+ {
+ goto fail;
+ }
+ if (next != NULL)
+ {
+ /* If newitem->child already set, then crosswire ->prev and ->next and move on */
+ next->next = newchild;
+ newchild->prev = next;
+ next = newchild;
+ }
+ else
+ {
+ /* Set newitem->child and move to it */
+ newitem->child = newchild;
+ next = newchild;
+ }
+ child = child->next;
+ }
+ if (newitem && newitem->child)
+ {
+ newitem->child->prev = newchild;
+ }
+
+ return newitem;
+
+fail:
+ if (newitem != NULL)
+ {
+ cJSON_Delete(newitem);
+ }
+
+ return NULL;
+}
+
+static void skip_oneline_comment(char **input)
+{
+ *input += static_strlen("//");
+
+ for (; (*input)[0] != '\0'; ++(*input))
+ {
+ if ((*input)[0] == '\n') {
+ *input += static_strlen("\n");
+ return;
+ }
+ }
+}
+
+static void skip_multiline_comment(char **input)
+{
+ *input += static_strlen("/*");
+
+ for (; (*input)[0] != '\0'; ++(*input))
+ {
+ if (((*input)[0] == '*') && ((*input)[1] == '/'))
+ {
+ *input += static_strlen("*/");
+ return;
+ }
+ }
+}
+
+static void minify_string(char **input, char **output) {
+ (*output)[0] = (*input)[0];
+ *input += static_strlen("\"");
+ *output += static_strlen("\"");
+
+
+ for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) {
+ (*output)[0] = (*input)[0];
+
+ if ((*input)[0] == '\"') {
+ (*output)[0] = '\"';
+ *input += static_strlen("\"");
+ *output += static_strlen("\"");
+ return;
+ } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) {
+ (*output)[1] = (*input)[1];
+ *input += static_strlen("\"");
+ *output += static_strlen("\"");
+ }
+ }
+}
+
+CJSON_PUBLIC(void) cJSON_Minify(char *json)
+{
+ char *into = json;
+
+ if (json == NULL)
+ {
+ return;
+ }
+
+ while (json[0] != '\0')
+ {
+ switch (json[0])
+ {
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ json++;
+ break;
+
+ case '/':
+ if (json[1] == '/')
+ {
+ skip_oneline_comment(&json);
+ }
+ else if (json[1] == '*')
+ {
+ skip_multiline_comment(&json);
+ } else {
+ json++;
+ }
+ break;
+
+ case '\"':
+ minify_string(&json, (char**)&into);
+ break;
+
+ default:
+ into[0] = json[0];
+ json++;
+ into++;
+ }
+ }
+
+ /* and null-terminate. */
+ *into = '\0';
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_Invalid;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_False;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xff) == cJSON_True;
+}
+
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & (cJSON_True | cJSON_False)) != 0;
+}
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_NULL;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_Number;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_String;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_Array;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_Object;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_Raw;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive)
+{
+ if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a))
+ {
+ return false;
+ }
+
+ /* check if type is valid */
+ switch (a->type & 0xFF)
+ {
+ case cJSON_False:
+ case cJSON_True:
+ case cJSON_NULL:
+ case cJSON_Number:
+ case cJSON_String:
+ case cJSON_Raw:
+ case cJSON_Array:
+ case cJSON_Object:
+ break;
+
+ default:
+ return false;
+ }
+
+ /* identical objects are equal */
+ if (a == b)
+ {
+ return true;
+ }
+
+ switch (a->type & 0xFF)
+ {
+ /* in these cases and equal type is enough */
+ case cJSON_False:
+ case cJSON_True:
+ case cJSON_NULL:
+ return true;
+
+ case cJSON_Number:
+ if (compare_double(a->valuedouble, b->valuedouble))
+ {
+ return true;
+ }
+ return false;
+
+ case cJSON_String:
+ case cJSON_Raw:
+ if ((a->valuestring == NULL) || (b->valuestring == NULL))
+ {
+ return false;
+ }
+ if (strcmp(a->valuestring, b->valuestring) == 0)
+ {
+ return true;
+ }
+
+ return false;
+
+ case cJSON_Array:
+ {
+ cJSON *a_element = a->child;
+ cJSON *b_element = b->child;
+
+ for (; (a_element != NULL) && (b_element != NULL);)
+ {
+ if (!cJSON_Compare(a_element, b_element, case_sensitive))
+ {
+ return false;
+ }
+
+ a_element = a_element->next;
+ b_element = b_element->next;
+ }
+
+ /* one of the arrays is longer than the other */
+ if (a_element != b_element) {
+ return false;
+ }
+
+ return true;
+ }
+
+ case cJSON_Object:
+ {
+ cJSON *a_element = NULL;
+ cJSON *b_element = NULL;
+ cJSON_ArrayForEach(a_element, a)
+ {
+ /* TODO This has O(n^2) runtime, which is horrible! */
+ b_element = get_object_item(b, a_element->string, case_sensitive);
+ if (b_element == NULL)
+ {
+ return false;
+ }
+
+ if (!cJSON_Compare(a_element, b_element, case_sensitive))
+ {
+ return false;
+ }
+ }
+
+ /* doing this twice, once on a and b to prevent true comparison if a subset of b
+ * TODO: Do this the proper way, this is just a fix for now */
+ cJSON_ArrayForEach(b_element, b)
+ {
+ a_element = get_object_item(a, b_element->string, case_sensitive);
+ if (a_element == NULL)
+ {
+ return false;
+ }
+
+ if (!cJSON_Compare(b_element, a_element, case_sensitive))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ default:
+ return false;
+ }
+}
+
+CJSON_PUBLIC(void *) cJSON_malloc(size_t size)
+{
+ return global_hooks.allocate(size);
+}
+
+CJSON_PUBLIC(void) cJSON_free(void *object)
+{
+ global_hooks.deallocate(object);
+}
diff --git a/depends/cJSON.h b/depends/cJSON.h
new file mode 100644
index 0000000..e97e5f4
--- /dev/null
+++ b/depends/cJSON.h
@@ -0,0 +1,293 @@
+/*
+ Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+#ifndef cJSON__h
+#define cJSON__h
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
+#define __WINDOWS__
+#endif
+
+#ifdef __WINDOWS__
+
+/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options:
+
+CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
+CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
+CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol
+
+For *nix builds that support visibility attribute, you can define similar behavior by
+
+setting default visibility to hidden by adding
+-fvisibility=hidden (for gcc)
+or
+-xldscope=hidden (for sun cc)
+to CFLAGS
+
+then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
+
+*/
+
+#define CJSON_CDECL __cdecl
+#define CJSON_STDCALL __stdcall
+
+/* export symbols by default, this is necessary for copy pasting the C and header file */
+#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
+#define CJSON_EXPORT_SYMBOLS
+#endif
+
+#if defined(CJSON_HIDE_SYMBOLS)
+#define CJSON_PUBLIC(type) type CJSON_STDCALL
+#elif defined(CJSON_EXPORT_SYMBOLS)
+#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL
+#elif defined(CJSON_IMPORT_SYMBOLS)
+#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL
+#endif
+#else /* !__WINDOWS__ */
+#define CJSON_CDECL
+#define CJSON_STDCALL
+
+#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
+#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type
+#else
+#define CJSON_PUBLIC(type) type
+#endif
+#endif
+
+/* project version */
+#define CJSON_VERSION_MAJOR 1
+#define CJSON_VERSION_MINOR 7
+#define CJSON_VERSION_PATCH 14
+
+#include <stddef.h>
+
+/* cJSON Types: */
+#define cJSON_Invalid (0)
+#define cJSON_False (1 << 0)
+#define cJSON_True (1 << 1)
+#define cJSON_NULL (1 << 2)
+#define cJSON_Number (1 << 3)
+#define cJSON_String (1 << 4)
+#define cJSON_Array (1 << 5)
+#define cJSON_Object (1 << 6)
+#define cJSON_Raw (1 << 7) /* raw json */
+
+#define cJSON_IsReference 256
+#define cJSON_StringIsConst 512
+
+/* The cJSON structure: */
+typedef struct cJSON
+{
+ /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
+ struct cJSON *next;
+ struct cJSON *prev;
+ /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
+ struct cJSON *child;
+
+ /* The type of the item, as above. */
+ int type;
+
+ /* The item's string, if type==cJSON_String and type == cJSON_Raw */
+ char *valuestring;
+ /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
+ int valueint;
+ /* The item's number, if type==cJSON_Number */
+ double valuedouble;
+
+ /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
+ char *string;
+} cJSON;
+
+typedef struct cJSON_Hooks
+{
+ /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */
+ void *(CJSON_CDECL *malloc_fn)(size_t sz);
+ void (CJSON_CDECL *free_fn)(void *ptr);
+} cJSON_Hooks;
+
+typedef int cJSON_bool;
+
+/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
+ * This is to prevent stack overflows. */
+#ifndef CJSON_NESTING_LIMIT
+#define CJSON_NESTING_LIMIT 1000
+#endif
+
+/* returns the version of cJSON as a string */
+CJSON_PUBLIC(const char*) cJSON_Version(void);
+
+/* Supply malloc, realloc and free functions to cJSON */
+CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
+
+/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
+/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
+CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
+/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
+/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated);
+
+/* Render a cJSON entity to text for transfer/storage. */
+CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
+/* Render a cJSON entity to text for transfer/storage without any formatting. */
+CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
+/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
+CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
+/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
+/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
+CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
+/* Delete a cJSON entity and all subentities. */
+CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
+
+/* Returns the number of items in an array (or object). */
+CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
+/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */
+CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
+/* Get item "string" from object. Case insensitive. */
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
+CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
+/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
+CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
+
+/* Check item type and return its value */
+CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item);
+CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item);
+
+/* These functions check the type of an item */
+CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
+
+/* These calls create a cJSON item of the appropriate type. */
+CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
+CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
+CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
+/* raw json */
+CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
+CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
+
+/* Create a string where valuestring references a string so
+ * it will not be freed by cJSON_Delete */
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
+/* Create an object/array that only references it's elements so
+ * they will not be freed by cJSON_Delete */
+CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
+CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
+
+/* These utilities create an Array of count items.
+ * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/
+CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
+CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
+CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count);
+
+/* Append item to the specified array/object. */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
+/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
+ * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
+ * writing to `item->string` */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
+/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
+
+/* Remove/Detach items from Arrays/Objects. */
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
+CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
+
+/* Update array items. */
+CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem);
+
+/* Duplicate a cJSON item */
+CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
+/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
+ * need to be released. With recurse!=0, it will duplicate any children connected to the item.
+ * The item->next and ->prev pointers are always zero on return from Duplicate. */
+/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
+ * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
+CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);
+
+/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings.
+ * The input pointer json cannot point to a read-only address area, such as a string constant,
+ * but should point to a readable and writable adress area. */
+CJSON_PUBLIC(void) cJSON_Minify(char *json);
+
+/* Helper functions for creating and adding items to an object at the same time.
+ * They return the added item or NULL on failure. */
+CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
+CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
+CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
+CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
+CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);
+
+/* When assigning an integer value, it needs to be propagated to valuedouble too. */
+#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
+/* helper for the cJSON_SetNumberValue macro */
+CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
+#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
+/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */
+CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring);
+
+/* Macro for iterating over an array or object */
+#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
+
+/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
+CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
+CJSON_PUBLIC(void) cJSON_free(void *object);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/html.c b/src/html.c
index b7abbdf..fc45843 100644
--- a/src/html.c
+++ b/src/html.c
@@ -5,10 +5,11 @@
#include "stringutils.h"
#include "rss_html_common.h"
#include "main.h"
-#include "json.h"
+#include "../depends/cJSON.h"
#include <limits.h>
#include <string.h>
#include <stdio.h>
+#include <stdlib.h>
#include <libgen.h>
#include <signal.h>
#include <time.h>
@@ -82,7 +83,7 @@ static int url_extract_domain(const char *url, char *domain, int domain_len) {
TODO: Rename input "title" to "url", to make input and output match (easier to test with).
*/
typedef int (*PluginListCallback)(const char *name, const char *url, void *userdata);
-static struct json_value_s* plugin_list(char *plugin_filepath, const char *url, struct json_array_s *downloaded_items, PluginListCallback callback, void *userdata) {
+static cJSON* plugin_list(char *plugin_filepath, const char *url, cJSON *downloaded_items, PluginListCallback callback, void *userdata) {
int result;
Buffer buffer;
buffer_init(&buffer);
@@ -97,12 +98,8 @@ static struct json_value_s* plugin_list(char *plugin_filepath, const char *url,
goto err_cleanup;
}
- if(downloaded_items) {
- struct json_value_s downloaded_items_value;
- downloaded_items_value.payload = downloaded_items;
- downloaded_items_value.type = json_type_array;
- size_t json_output_len = 0;
- void *json_body_str = json_write_minified(&downloaded_items_value, &json_output_len);
+ if(cJSON_IsArray(downloaded_items)) {
+ char *json_body_str = cJSON_PrintUnformatted(downloaded_items);
if(!json_body_str) {
fprintf(stderr, "Failed to convert downloaded items to json\n");
if(process_id != -1)
@@ -111,10 +108,10 @@ static struct json_value_s* plugin_list(char *plugin_filepath, const char *url,
close(stdout_file);
goto err_cleanup;
}
+ size_t json_output_len = strlen(json_body_str);
- /* This is a bug in the json library */
- json_output_len = strlen(json_body_str);
if(write(stdin_file, json_body_str, json_output_len) != (ssize_t)json_output_len) {
+ free(json_body_str);
fprintf(stderr, "Failed to write all bytes to plugin list\n");
if(process_id != -1)
kill(process_id, SIGKILL);
@@ -131,41 +128,34 @@ static struct json_value_s* plugin_list(char *plugin_filepath, const char *url,
goto err_cleanup;
}
- struct json_value_s *json_root = json_parse(buffer.data, buffer.size);
+ cJSON *json_root = cJSON_ParseWithLength(buffer.data, buffer.size);
if(!json_root) {
fprintf(stderr, "Failed to load plugin %s list output as json\n", basename(plugin_filepath));
goto err_cleanup;
}
buffer_deinit(&buffer);
- struct json_array_s *json_root_array = json_value_as_array(json_root);
- if(!json_root_array) {
+ if(!cJSON_IsArray(json_root)) {
fprintf(stderr, "Failed to load plugin %s list output as json\n", basename(plugin_filepath));
- free(json_root);
+ cJSON_Delete(json_root);
goto err_cleanup;
}
- struct json_array_element_s *json_array_element = json_root_array->start;
- for(; json_array_element; json_array_element = json_array_element->next) {
- struct json_object_s *array_element_value = json_value_as_object(json_array_element->value);
- if(!array_element_value)
- continue;
-
- struct json_value_s *name_json = json_object_get_field_by_name(array_element_value, "name");
- struct json_value_s *url_json = json_object_get_field_by_name(array_element_value, "url");
- if(!name_json || !url_json)
+ cJSON *array_item = NULL;
+ cJSON_ArrayForEach(array_item, json_root) {
+ if(!cJSON_IsObject(array_item))
continue;
- struct json_string_s *name_json_str = json_value_as_string(name_json);
- struct json_string_s *url_json_str = json_value_as_string(url_json);
- if(!name_json_str || !url_json_str)
+ cJSON *name_json = cJSON_GetObjectItemCaseSensitive(array_item, "name");
+ cJSON *url_json = cJSON_GetObjectItemCaseSensitive(array_item, "url");
+ if(!cJSON_IsString(name_json) || !cJSON_IsString(url_json))
continue;
/* Cast from const to mutable variable because I can */
- char *name = (char*)name_json_str->string;
+ char *name = name_json->valuestring;
string_replace(name, '/', '_');
name = strip(name);
- if(callback(name, url_json_str->string, userdata) != 0)
+ if(callback(name, url_json->valuestring, userdata) != 0)
break;
}
@@ -245,7 +235,7 @@ int add_html(const char *name, const char *url, char *html_config_dir, char *pro
plugin_list_userdata.found_start_after = 0;
plugin_list_userdata.start_after_url = NULL;
- struct json_value_s *json_root = plugin_list(domain_plugin_path, url, NULL, plugin_list_callback, &plugin_list_userdata);
+ cJSON *json_root = plugin_list(domain_plugin_path, url, NULL, plugin_list_callback, &plugin_list_userdata);
if(!json_root)
return -1;
@@ -322,7 +312,7 @@ int add_html(const char *name, const char *url, char *html_config_dir, char *pro
remove(in_progress_filepath);
cleanup:
- free(json_root);
+ cJSON_Delete(json_root);
return result;
}
@@ -407,14 +397,10 @@ int sync_html(TrackedHtml *tracked_html, char *program_dir, const char *download
if(get_plugin_filepath(program_dir, tracked_html->plugin, plugin_filepath) != 0)
return -1;
- struct json_value_s *downloaded_items = json_object_get_field_by_name(tracked_html->json_data, "downloaded");
- struct json_array_s *downloaded_items_array = NULL;
- if(downloaded_items) {
- downloaded_items_array = json_value_as_array(downloaded_items);
- if(!downloaded_items_array) {
- fprintf(stderr, "Corrupt json for html item: %s\n", tracked_html->title);
- return -1;
- }
+ cJSON *downloaded_items = cJSON_GetObjectItemCaseSensitive(tracked_html->json_data, "downloaded");
+ if(!cJSON_IsArray(downloaded_items)) {
+ fprintf(stderr, "Corrupt json for html item: %s\n", tracked_html->title);
+ return -1;
}
Buffer download_items_buffer;
@@ -422,7 +408,7 @@ int sync_html(TrackedHtml *tracked_html, char *program_dir, const char *download
int result = 0;
- struct json_value_s *json_root = plugin_list(plugin_filepath, tracked_html->link, downloaded_items_array, plugin_list_sync_callback, &download_items_buffer);
+ cJSON *json_root = plugin_list(plugin_filepath, tracked_html->link, downloaded_items, plugin_list_sync_callback, &download_items_buffer);
if(!json_root) {
result = -1;
goto cleanup;
@@ -447,7 +433,7 @@ int sync_html(TrackedHtml *tracked_html, char *program_dir, const char *download
}
cleanup:
- free(json_root);
+ cJSON_Delete(json_root);
buffer_deinit(&download_items_buffer);
html_config_dir[html_config_dir_len] = '\0';
return result;
diff --git a/src/html.h b/src/html.h
index 987f281..327a39e 100644
--- a/src/html.h
+++ b/src/html.h
@@ -1,11 +1,13 @@
#ifndef HTML_H
#define HTML_H
+typedef struct cJSON cJSON;
+
typedef struct {
char *plugin;
char *title;
char *link;
- struct json_object_s *json_data;
+ cJSON *json_data;
} TrackedHtml;
int add_html(const char *name, const char *url, char *html_config_dir, char *program_dir, const char *start_after);
diff --git a/src/json.h b/src/json.h
deleted file mode 100644
index 7ea755c..0000000
--- a/src/json.h
+++ /dev/null
@@ -1,3077 +0,0 @@
-/*
- The latest version of this library is available on GitHub;
- https://github.com/sheredom/json.h.
-*/
-
-/*
- This is free and unencumbered software released into the public domain.
-
- Anyone is free to copy, modify, publish, use, compile, sell, or
- distribute this software, either in source code form or as a compiled
- binary, for any purpose, commercial or non-commercial, and by any
- means.
-
- In jurisdictions that recognize copyright laws, the author or authors
- of this software dedicate any and all copyright interest in the
- software to the public domain. We make this dedication for the benefit
- of the public at large and to the detriment of our heirs and
- successors. We intend this dedication to be an overt act of
- relinquishment in perpetuity of all present and future rights to this
- software under copyright law.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
- IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
- OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- OTHER DEALINGS IN THE SOFTWARE.
-
- For more information, please refer to <http://unlicense.org/>.
-*/
-
-#ifndef SHEREDOM_JSON_H_INCLUDED
-#define SHEREDOM_JSON_H_INCLUDED
-
-#if defined(_MSC_VER)
-#pragma warning(push)
-
-/* disable 'bytes padding added after construct' warning */
-#pragma warning(disable : 4820)
-#endif
-
-#include <stddef.h>
-
-#if defined(__clang__) || defined(__GNUC__)
-#define json_weak __attribute__((weak))
-#elif defined(_MSC_VER)
-#define json_weak __inline
-#else
-#error Non clang, non gcc, non MSVC compiler found!
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-struct json_value_s;
-struct json_parse_result_s;
-
-enum json_parse_flags_e {
- json_parse_flags_default = 0,
-
- /* allow trailing commas in objects and arrays. For example, both [true,] and
- {"a" : null,} would be allowed with this option on. */
- json_parse_flags_allow_trailing_comma = 0x1,
-
- /* allow unquoted keys for objects. For example, {a : null} would be allowed
- with this option on. */
- json_parse_flags_allow_unquoted_keys = 0x2,
-
- /* allow a global unbracketed object. For example, a : null, b : true, c : {}
- would be allowed with this option on. */
- json_parse_flags_allow_global_object = 0x4,
-
- /* allow objects to use '=' instead of ':' between key/value pairs. For
- example, a = null, b : true would be allowed with this option on. */
- json_parse_flags_allow_equals_in_object = 0x8,
-
- /* allow that objects don't have to have comma separators between key/value
- pairs. */
- json_parse_flags_allow_no_commas = 0x10,
-
- /* allow c-style comments (either variants) to be ignored in the input JSON
- file. */
- json_parse_flags_allow_c_style_comments = 0x20,
-
- /* deprecated flag, unused. */
- json_parse_flags_deprecated = 0x40,
-
- /* record location information for each value. */
- json_parse_flags_allow_location_information = 0x80,
-
- /* allow strings to be 'single quoted'. */
- json_parse_flags_allow_single_quoted_strings = 0x100,
-
- /* allow numbers to be hexadecimal. */
- json_parse_flags_allow_hexadecimal_numbers = 0x200,
-
- /* allow numbers like +123 to be parsed. */
- json_parse_flags_allow_leading_plus_sign = 0x400,
-
- /* allow numbers like .0123 or 123. to be parsed. */
- json_parse_flags_allow_leading_or_trailing_decimal_point = 0x800,
-
- /* allow Infinity, -Infinity, NaN, -NaN. */
- json_parse_flags_allow_inf_and_nan = 0x1000,
-
- /* allow multi line string values. */
- json_parse_flags_allow_multi_line_strings = 0x2000,
-
- /* allow simplified JSON to be parsed. Simplified JSON is an enabling of a set
- of other parsing options. */
- json_parse_flags_allow_simplified_json =
- (json_parse_flags_allow_trailing_comma |
- json_parse_flags_allow_unquoted_keys |
- json_parse_flags_allow_global_object |
- json_parse_flags_allow_equals_in_object |
- json_parse_flags_allow_no_commas),
-
- /* allow JSON5 to be parsed. JSON5 is an enabling of a set of other parsing
- options. */
- json_parse_flags_allow_json5 =
- (json_parse_flags_allow_trailing_comma |
- json_parse_flags_allow_unquoted_keys |
- json_parse_flags_allow_c_style_comments |
- json_parse_flags_allow_single_quoted_strings |
- json_parse_flags_allow_hexadecimal_numbers |
- json_parse_flags_allow_leading_plus_sign |
- json_parse_flags_allow_leading_or_trailing_decimal_point |
- json_parse_flags_allow_inf_and_nan |
- json_parse_flags_allow_multi_line_strings)
-};
-
-/* Parse a JSON text file, returning a pointer to the root of the JSON
- * structure. json_parse performs 1 call to malloc for the entire encoding.
- * Returns 0 if an error occurred (malformed JSON input, or malloc failed). */
-json_weak struct json_value_s *json_parse(const void *src, size_t src_size);
-
-/* Parse a JSON text file, returning a pointer to the root of the JSON
- * structure. json_parse performs 1 call to malloc for the entire encoding.
- * Returns 0 if an error occurred (malformed JSON input, or malloc failed). If
- * an error occurred, the result struct (if not NULL) will explain the type of
- * error, and the location in the input it occurred. */
-json_weak struct json_value_s *
-json_parse_ex(const void *src, size_t src_size, size_t flags_bitset,
- void *(*alloc_func_ptr)(void *, size_t), void *user_data,
- struct json_parse_result_s *result);
-
-/* Write out a minified JSON utf-8 string. This string is an encoding of the
- * minimal string characters required to still encode the same data.
- * json_write_minified performs 1 call to malloc for the entire encoding. Return
- * 0 if an error occurred (malformed JSON input, or malloc failed). The out_size
- * parameter is optional as the utf-8 string is null terminated. */
-json_weak void *json_write_minified(const struct json_value_s *value,
- size_t *out_size);
-
-/* Write out a pretty JSON utf-8 string. This string is encoded such that the
- * resultant JSON is pretty in that it is easily human readable. The indent and
- * newline parameters allow a user to specify what kind of indentation and
- * newline they want (two spaces / three spaces / tabs? \r, \n, \r\n ?). Both
- * indent and newline can be NULL, indent defaults to two spaces (" "), and
- * newline defaults to linux newlines ('\n' as the newline character).
- * json_write_pretty performs 1 call to malloc for the entire encoding. Return 0
- * if an error occurred (malformed JSON input, or malloc failed). The out_size
- * parameter is optional as the utf-8 string is null terminated. */
-json_weak void *json_write_pretty(const struct json_value_s *value,
- const char *indent, const char *newline,
- size_t *out_size);
-
-/* Reinterpret a JSON value as a string. Returns null is the value was not a
- * string. */
-json_weak struct json_string_s *
-json_value_as_string(struct json_value_s *const value);
-
-/* Reinterpret a JSON value as a number. Returns null is the value was not a
- * number. */
-json_weak struct json_number_s *
-json_value_as_number(struct json_value_s *const value);
-
-/* Reinterpret a JSON value as an object. Returns null is the value was not an
- * object. */
-json_weak struct json_object_s *
-json_value_as_object(struct json_value_s *const value);
-
-/* Reinterpret a JSON value as an array. Returns null is the value was not an
- * array. */
-json_weak struct json_array_s *
-json_value_as_array(struct json_value_s *const value);
-
-/* Whether the value is true. */
-json_weak int json_value_is_true(const struct json_value_s *const value);
-
-/* Whether the value is false. */
-json_weak int json_value_is_false(const struct json_value_s *const value);
-
-/* Whether the value is null. */
-json_weak int json_value_is_null(const struct json_value_s *const value);
-
-/* The various types JSON values can be. Used to identify what a value is. */
-enum json_type_e {
- json_type_string,
- json_type_number,
- json_type_object,
- json_type_array,
- json_type_true,
- json_type_false,
- json_type_null
-};
-
-/* A JSON string value. */
-struct json_string_s {
- /* utf-8 string */
- const char *string;
- /* The size (in bytes) of the string */
- size_t string_size;
-};
-
-/* A JSON string value (extended). */
-struct json_string_ex_s {
- /* The JSON string this extends. */
- struct json_string_s string;
-
- /* The character offset for the value in the JSON input. */
- size_t offset;
-
- /* The line number for the value in the JSON input. */
- size_t line_no;
-
- /* The row number for the value in the JSON input, in bytes. */
- size_t row_no;
-};
-
-/* A JSON number value. */
-struct json_number_s {
- /* ASCII string containing representation of the number. */
- const char *number;
- /* the size (in bytes) of the number. */
- size_t number_size;
-};
-
-/* an element of a JSON object. */
-struct json_object_element_s {
- /* the name of this element. */
- struct json_string_s *name;
- /* the value of this element. */
- struct json_value_s *value;
- /* the next object element (can be NULL if the last element in the object). */
- struct json_object_element_s *next;
-};
-
-/* a JSON object value. */
-struct json_object_s {
- /* a linked list of the elements in the object. */
- struct json_object_element_s *start;
- /* the number of elements in the object. */
- size_t length;
-};
-
-/* an element of a JSON array. */
-struct json_array_element_s {
- /* the value of this element. */
- struct json_value_s *value;
- /* the next array element (can be NULL if the last element in the array). */
- struct json_array_element_s *next;
-};
-
-/* a JSON array value. */
-struct json_array_s {
- /* a linked list of the elements in the array. */
- struct json_array_element_s *start;
- /* the number of elements in the array. */
- size_t length;
-};
-
-/* a JSON value. */
-struct json_value_s {
- /* a pointer to either a json_string_s, json_number_s, json_object_s, or. */
- /* json_array_s. Should be cast to the appropriate struct type based on what.
- */
- /* the type of this value is. */
- void *payload;
- /* must be one of json_type_e. If type is json_type_true, json_type_false, or.
- */
- /* json_type_null, payload will be NULL. */
- size_t type;
-};
-
-/* a JSON value (extended). */
-struct json_value_ex_s {
- /* the JSON value this extends. */
- struct json_value_s value;
-
- /* the character offset for the value in the JSON input. */
- size_t offset;
-
- /* the line number for the value in the JSON input. */
- size_t line_no;
-
- /* the row number for the value in the JSON input, in bytes. */
- size_t row_no;
-};
-
-/* a parsing error code. */
-enum json_parse_error_e {
- /* no error occurred (huzzah!). */
- json_parse_error_none = 0,
-
- /* expected either a comma or a closing '}' or ']' to close an object or. */
- /* array! */
- json_parse_error_expected_comma_or_closing_bracket,
-
- /* colon separating name/value pair was missing! */
- json_parse_error_expected_colon,
-
- /* expected string to begin with '"'! */
- json_parse_error_expected_opening_quote,
-
- /* invalid escaped sequence in string! */
- json_parse_error_invalid_string_escape_sequence,
-
- /* invalid number format! */
- json_parse_error_invalid_number_format,
-
- /* invalid value! */
- json_parse_error_invalid_value,
-
- /* reached end of buffer before object/array was complete! */
- json_parse_error_premature_end_of_buffer,
-
- /* string was malformed! */
- json_parse_error_invalid_string,
-
- /* a call to malloc, or a user provider allocator, failed. */
- json_parse_error_allocator_failed,
-
- /* the JSON input had unexpected trailing characters that weren't part of the.
- */
- /* JSON value. */
- json_parse_error_unexpected_trailing_characters,
-
- /* catch-all error for everything else that exploded (real bad chi!). */
- json_parse_error_unknown
-};
-
-/* error report from json_parse_ex(). */
-struct json_parse_result_s {
- /* the error code (one of json_parse_error_e). */
- size_t error;
-
- /* the character offset for the error in the JSON input. */
- size_t error_offset;
-
- /* the line number for the error in the JSON input. */
- size_t error_line_no;
-
- /* the row number for the error, in bytes. */
- size_t error_row_no;
-};
-
-#ifdef __cplusplus
-} /* extern "C". */
-#endif
-
-#if defined(_MSC_VER)
-#pragma warning(pop)
-#endif
-
-#include <stdlib.h>
-
-#if defined(_MSC_VER)
-#define json_strtoumax _strtoui64
-#define json_uintmax_t unsigned __int64
-#else
-#include <inttypes.h>
-#define json_strtoumax strtoumax
-#define json_uintmax_t uintmax_t
-#endif
-
-#if defined(__cplusplus) && (__cplusplus >= 201103L)
-#define json_null nullptr
-#else
-#define json_null 0
-#endif
-
-#if defined(__clang__)
-#pragma clang diagnostic push
-
-/* we do one big allocation via malloc, then cast aligned slices of this for. */
-/* our structures - we don't have a way to tell the compiler we know what we. */
-/* are doing, so disable the warning instead! */
-#pragma clang diagnostic ignored "-Wcast-align"
-
-/* We use C style casts everywhere. */
-#pragma clang diagnostic ignored "-Wold-style-cast"
-
-/* We need long long for strtoull. */
-#pragma clang diagnostic ignored "-Wc++11-long-long"
-
-/* Who cares if nullptr doesn't work with C++98, we don't use it there! */
-#pragma clang diagnostic ignored "-Wc++98-compat"
-#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
-#elif defined(_MSC_VER)
-#pragma warning(push)
-
-/* disable 'function selected for inline expansion' warning. */
-#pragma warning(disable : 4711)
-
-/* disable '#pragma warning: there is no warning number' warning. */
-#pragma warning(disable : 4619)
-
-/* disable 'warning number not a valid compiler warning' warning. */
-#pragma warning(disable : 4616)
-
-/* disable 'Compiler will insert Spectre mitigation for memory load if
- * /Qspectre. */
-/* switch specified' warning. */
-#pragma warning(disable : 5045)
-#endif
-
-struct json_parse_state_s {
- const char *src;
- size_t size;
- size_t offset;
- size_t flags_bitset;
- char *data;
- char *dom;
- size_t dom_size;
- size_t data_size;
- size_t line_no; /* line counter for error reporting. */
- size_t line_offset; /* (offset-line_offset) is the character number (in
- bytes). */
- size_t error;
-};
-
-json_weak int json_hexadecimal_digit(const char c);
-int json_hexadecimal_digit(const char c) {
- if ('0' <= c && c <= '9') {
- return c - '0';
- }
- if ('a' <= c && c <= 'f') {
- return c - 'a' + 10;
- }
- if ('A' <= c && c <= 'F') {
- return c - 'A' + 10;
- }
- return -1;
-}
-
-json_weak int json_hexadecimal_value(const char * c, const unsigned long size, unsigned long * result);
- int json_hexadecimal_value(const char * c, const unsigned long size, unsigned long * result) {
- const char * p;
- int digit;
-
- if (size > sizeof(unsigned long) * 2) {
- return 0;
- }
-
- *result = 0;
- for (p = c; (unsigned long)(p - c) < size; ++p) {
- *result <<= 4;
- digit = json_hexadecimal_digit(*p);
- if (digit < 0 || digit > 15) {
- return 0;
- }
- *result |= (unsigned char)digit;
- }
- return 1;
-}
-
-json_weak int json_skip_whitespace(struct json_parse_state_s *state);
- int json_skip_whitespace(struct json_parse_state_s *state) {
- size_t offset = state->offset;
- const size_t size = state->size;
- const char *const src = state->src;
-
- /* the only valid whitespace according to ECMA-404 is ' ', '\n', '\r' and
- * '\t'. */
- switch (src[offset]) {
- default:
- return 0;
- case ' ':
- case '\r':
- case '\t':
- case '\n':
- break;
- }
-
- do {
- switch (src[offset]) {
- default:
- /* Update offset. */
- state->offset = offset;
- return 1;
- case ' ':
- case '\r':
- case '\t':
- break;
- case '\n':
- state->line_no++;
- state->line_offset = offset;
- break;
- }
-
- offset++;
- } while (offset < size);
-
- /* Update offset. */
- state->offset = offset;
- return 1;
-}
-
-json_weak int json_skip_c_style_comments(struct json_parse_state_s *state);
- int json_skip_c_style_comments(struct json_parse_state_s *state) {
- /* do we have a comment?. */
- if ('/' == state->src[state->offset]) {
- /* skip '/'. */
- state->offset++;
-
- if ('/' == state->src[state->offset]) {
- /* we had a comment of the form //. */
-
- /* skip second '/'. */
- state->offset++;
-
- while (state->offset < state->size) {
- switch (state->src[state->offset]) {
- default:
- /* skip the character in the comment. */
- state->offset++;
- break;
- case '\n':
- /* if we have a newline, our comment has ended! Skip the newline. */
- state->offset++;
-
- /* we entered a newline, so move our line info forward. */
- state->line_no++;
- state->line_offset = state->offset;
- return 1;
- }
- }
-
- /* we reached the end of the JSON file! */
- return 1;
- } else if ('*' == state->src[state->offset]) {
- /* we had a comment in the C-style long form. */
-
- /* skip '*'. */
- state->offset++;
-
- while (state->offset + 1 < state->size) {
- if (('*' == state->src[state->offset]) &&
- ('/' == state->src[state->offset + 1])) {
- /* we reached the end of our comment! */
- state->offset += 2;
- return 1;
- } else if ('\n' == state->src[state->offset]) {
- /* we entered a newline, so move our line info forward. */
- state->line_no++;
- state->line_offset = state->offset;
- }
-
- /* skip character within comment. */
- state->offset++;
- }
-
- /* Comment wasn't ended correctly which is a failure. */
- return 1;
- }
- }
-
- /* we didn't have any comment, which is ok too! */
- return 0;
-}
-
-json_weak int json_skip_all_skippables(struct json_parse_state_s *state);
- int json_skip_all_skippables(struct json_parse_state_s *state) {
- /* skip all whitespace and other skippables until there are none left. note
- * that the previous version suffered from read past errors should. the
- * stream end on json_skip_c_style_comments eg. '{"a" ' with comments flag.
- */
-
- int did_consume = 0;
- const size_t size = state->size;
-
- if (json_parse_flags_allow_c_style_comments & state->flags_bitset) {
- do {
- if (state->offset == size) {
- state->error = json_parse_error_premature_end_of_buffer;
- return 1;
- }
-
- did_consume = json_skip_whitespace(state);
-
- /* This should really be checked on access, not in front of every call.
- */
- if (state->offset == size) {
- state->error = json_parse_error_premature_end_of_buffer;
- return 1;
- }
-
- did_consume |= json_skip_c_style_comments(state);
- } while (0 != did_consume);
- } else {
- do {
- if (state->offset == size) {
- state->error = json_parse_error_premature_end_of_buffer;
- return 1;
- }
-
- did_consume = json_skip_whitespace(state);
- } while (0 != did_consume);
- }
-
- if (state->offset == size) {
- state->error = json_parse_error_premature_end_of_buffer;
- return 1;
- }
-
- return 0;
-}
-
-json_weak int json_get_value_size(struct json_parse_state_s *state,
- int is_global_object);
-
-json_weak int json_get_string_size(struct json_parse_state_s *state,
- size_t is_key);
-int json_get_string_size(struct json_parse_state_s *state,
- size_t is_key) {
- size_t offset = state->offset;
- const size_t size = state->size;
- size_t data_size = 0;
- const char *const src = state->src;
- const int is_single_quote = '\'' == src[offset];
- const char quote_to_use = is_single_quote ? '\'' : '"';
- const size_t flags_bitset = state->flags_bitset;
- unsigned long codepoint;
- unsigned long high_surrogate = 0;
-
- if ((json_parse_flags_allow_location_information & flags_bitset) != 0 &&
- is_key != 0) {
- state->dom_size += sizeof(struct json_string_ex_s);
- } else {
- state->dom_size += sizeof(struct json_string_s);
- }
-
- if ('"' != src[offset]) {
- /* if we are allowed single quoted strings check for that too. */
- if (!((json_parse_flags_allow_single_quoted_strings & flags_bitset) &&
- is_single_quote)) {
- state->error = json_parse_error_expected_opening_quote;
- state->offset = offset;
- return 1;
- }
- }
-
- /* skip leading '"' or '\''. */
- offset++;
-
- while ((offset < size) && (quote_to_use != src[offset])) {
- /* add space for the character. */
- data_size++;
-
- if ('\\' == src[offset]) {
- /* skip reverse solidus character. */
- offset++;
-
- if (offset == size) {
- state->error = json_parse_error_premature_end_of_buffer;
- state->offset = offset;
- return 1;
- }
-
- switch (src[offset]) {
- default:
- state->error = json_parse_error_invalid_string_escape_sequence;
- state->offset = offset;
- return 1;
- case '"':
- case '\\':
- case '/':
- case 'b':
- case 'f':
- case 'n':
- case 'r':
- case 't':
- /* all valid characters! */
- offset++;
- break;
- case 'u':
- if (!(offset + 5 < size)) {
- /* invalid escaped unicode sequence! */
- state->error = json_parse_error_invalid_string_escape_sequence;
- state->offset = offset;
- return 1;
- }
-
- codepoint = 0;
- if (!json_hexadecimal_value(&src[offset + 1], 4, &codepoint)) {
- /* escaped unicode sequences must contain 4 hexadecimal digits! */
- state->error = json_parse_error_invalid_string_escape_sequence;
- state->offset = offset;
- return 1;
- }
-
- /* Valid sequence!
- * see: https://en.wikipedia.org/wiki/UTF-8#Invalid_code_points.
- * 1 7 U + 0000 U + 007F 0xxxxxxx.
- * 2 11 U + 0080 U + 07FF 110xxxxx
- * 10xxxxxx.
- * 3 16 U + 0800 U + FFFF 1110xxxx
- * 10xxxxxx 10xxxxxx.
- * 4 21 U + 10000 U + 10FFFF 11110xxx
- * 10xxxxxx 10xxxxxx 10xxxxxx.
- * Note: the high and low surrogate halves used by UTF-16 (U+D800
- * through U+DFFF) and code points not encodable by UTF-16 (those after
- * U+10FFFF) are not legal Unicode values, and their UTF-8 encoding must
- * be treated as an invalid byte sequence. */
-
- if (high_surrogate != 0) {
- /* we previously read the high half of the \uxxxx\uxxxx pair, so now
- * we expect the low half. */
- if (codepoint >= 0xdc00 &&
- codepoint <= 0xdfff) { /* low surrogate range. */
- data_size += 3;
- high_surrogate = 0;
- } else {
- state->error = json_parse_error_invalid_string_escape_sequence;
- state->offset = offset;
- return 1;
- }
- } else if (codepoint <= 0x7f) {
- data_size += 0;
- } else if (codepoint <= 0x7ff) {
- data_size += 1;
- } else if (codepoint >= 0xd800 &&
- codepoint <= 0xdbff) { /* high surrogate range. */
- /* The codepoint is the first half of a "utf-16 surrogate pair". so we
- * need the other half for it to be valid: \uHHHH\uLLLL. */
- if (offset + 11 > size || '\\' != src[offset + 5] ||
- 'u' != src[offset + 6]) {
- state->error = json_parse_error_invalid_string_escape_sequence;
- state->offset = offset;
- return 1;
- }
- high_surrogate = codepoint;
- } else if (codepoint >= 0xd800 &&
- codepoint <= 0xdfff) { /* low surrogate range. */
- /* we did not read the other half before. */
- state->error = json_parse_error_invalid_string_escape_sequence;
- state->offset = offset;
- return 1;
- } else {
- data_size += 2;
- }
- /* escaped codepoints after 0xffff are supported in json through utf-16
- * surrogate pairs: \uD83D\uDD25 for U+1F525. */
-
- offset += 5;
- break;
- }
- } else if (('\r' == src[offset]) || ('\n' == src[offset])) {
- if (!(json_parse_flags_allow_multi_line_strings & flags_bitset)) {
- /* invalid escaped unicode sequence! */
- state->error = json_parse_error_invalid_string_escape_sequence;
- state->offset = offset;
- return 1;
- }
-
- offset++;
- } else {
- /* skip character (valid part of sequence). */
- offset++;
- }
- }
-
- /* skip trailing '"' or '\''. */
- offset++;
-
- /* add enough space to store the string. */
- state->data_size += data_size;
-
- /* one more byte for null terminator ending the string! */
- state->data_size++;
-
- /* update offset. */
- state->offset = offset;
-
- return 0;
-}
-
-json_weak int is_valid_unquoted_key_char(const char c);
- int is_valid_unquoted_key_char(const char c) {
- return (('0' <= c && c <= '9') || ('a' <= c && c <= 'z') ||
- ('A' <= c && c <= 'Z') || ('_' == c));
-}
-
-json_weak int json_get_key_size(struct json_parse_state_s *state);
- int json_get_key_size(struct json_parse_state_s *state) {
- const size_t flags_bitset = state->flags_bitset;
-
- if (json_parse_flags_allow_unquoted_keys & flags_bitset) {
- size_t offset = state->offset;
- const size_t size = state->size;
- const char *const src = state->src;
- size_t data_size = state->data_size;
-
- /* if we are allowing unquoted keys, first grok for a quote... */
- if ('"' == src[offset]) {
- /* ... if we got a comma, just parse the key as a string as normal. */
- return json_get_string_size(state, 1);
- } else if ((json_parse_flags_allow_single_quoted_strings & flags_bitset) &&
- ('\'' == src[offset])) {
- /* ... if we got a comma, just parse the key as a string as normal. */
- return json_get_string_size(state, 1);
- } else {
- while ((offset < size) && is_valid_unquoted_key_char(src[offset])) {
- offset++;
- data_size++;
- }
-
- /* one more byte for null terminator ending the string! */
- data_size++;
-
- if (json_parse_flags_allow_location_information & flags_bitset) {
- state->dom_size += sizeof(struct json_string_ex_s);
- } else {
- state->dom_size += sizeof(struct json_string_s);
- }
-
- /* update offset. */
- state->offset = offset;
-
- /* update data_size. */
- state->data_size = data_size;
-
- return 0;
- }
- } else {
- /* we are only allowed to have quoted keys, so just parse a string! */
- return json_get_string_size(state, 1);
- }
-}
-
-json_weak int json_get_object_size(struct json_parse_state_s *state,
- int is_global_object);
- int json_get_object_size(struct json_parse_state_s *state,
- int is_global_object) {
- const size_t flags_bitset = state->flags_bitset;
- const char *const src = state->src;
- const size_t size = state->size;
- size_t elements = 0;
- int allow_comma = 0;
-
- if (is_global_object) {
- /* if we found an opening '{' of an object, we actually have a normal JSON
- * object at the root of the DOM... */
- if (!json_skip_all_skippables(state) && '{' == state->src[state->offset]) {
- /* . and we don't actually have a global object after all! */
- is_global_object = 0;
- }
- }
-
- if (!is_global_object) {
- if ('{' != src[state->offset]) {
- state->error = json_parse_error_unknown;
- return 1;
- }
-
- /* skip leading '{'. */
- state->offset++;
- }
-
- state->dom_size += sizeof(struct json_object_s);
-
- while (state->offset < size) {
- if (!is_global_object) {
- if (json_skip_all_skippables(state)) {
- state->error = json_parse_error_premature_end_of_buffer;
- return 1;
- }
- if ('}' == src[state->offset]) {
- /* skip trailing '}'. */
- state->offset++;
-
- /* finished the object! */
- break;
- }
- } else {
- /* we don't require brackets, so that means the object ends when the input
- * stream ends! */
- if (json_skip_all_skippables(state)) {
- break;
- }
- }
-
- /* if we parsed at least once element previously, grok for a comma. */
- if (allow_comma) {
- if (',' == src[state->offset]) {
- /* skip comma. */
- state->offset++;
- allow_comma = 0;
- } else if (json_parse_flags_allow_no_commas & flags_bitset) {
- /* we don't require a comma, and we didn't find one, which is ok! */
- allow_comma = 0;
- } else {
- /* otherwise we are required to have a comma, and we found none. */
- state->error = json_parse_error_expected_comma_or_closing_bracket;
- return 1;
- }
-
- if (json_parse_flags_allow_trailing_comma & flags_bitset) {
- continue;
- } else {
- if (json_skip_all_skippables(state)) {
- state->error = json_parse_error_premature_end_of_buffer;
- return 1;
- }
- }
- }
-
- if (json_get_key_size(state)) {
- /* key parsing failed! */
- state->error = json_parse_error_invalid_string;
- return 1;
- }
-
- if (json_skip_all_skippables(state)) {
- state->error = json_parse_error_premature_end_of_buffer;
- return 1;
- }
-
- if (json_parse_flags_allow_equals_in_object & flags_bitset) {
- const char current = src[state->offset];
- if ((':' != current) && ('=' != current)) {
- state->error = json_parse_error_expected_colon;
- return 1;
- }
- } else {
- if (':' != src[state->offset]) {
- state->error = json_parse_error_expected_colon;
- return 1;
- }
- }
-
- /* skip colon. */
- state->offset++;
-
- if (json_skip_all_skippables(state)) {
- state->error = json_parse_error_premature_end_of_buffer;
- return 1;
- }
-
- if (json_get_value_size(state, /* is_global_object = */ 0)) {
- /* value parsing failed! */
- return 1;
- }
-
- /* successfully parsed a name/value pair! */
- elements++;
- allow_comma = 1;
- }
-
- state->dom_size += sizeof(struct json_object_element_s) * elements;
-
- return 0;
-}
-
-json_weak int json_get_array_size(struct json_parse_state_s *state);
- int json_get_array_size(struct json_parse_state_s *state) {
- const size_t flags_bitset = state->flags_bitset;
- size_t elements = 0;
- int allow_comma = 0;
- const char *const src = state->src;
- const size_t size = state->size;
-
- if ('[' != src[state->offset]) {
- /* expected array to begin with leading '['. */
- state->error = json_parse_error_unknown;
- return 1;
- }
-
- /* skip leading '['. */
- state->offset++;
-
- state->dom_size += sizeof(struct json_array_s);
-
- while (state->offset < size) {
- if (json_skip_all_skippables(state)) {
- state->error = json_parse_error_premature_end_of_buffer;
- return 1;
- }
-
- if (']' == src[state->offset]) {
- /* skip trailing ']'. */
- state->offset++;
-
- state->dom_size += sizeof(struct json_array_element_s) * elements;
-
- /* finished the object! */
- return 0;
- }
-
- /* if we parsed at least once element previously, grok for a comma. */
- if (allow_comma) {
- if (',' == src[state->offset]) {
- /* skip comma. */
- state->offset++;
- allow_comma = 0;
- } else if (!(json_parse_flags_allow_no_commas & flags_bitset)) {
- state->error = json_parse_error_expected_comma_or_closing_bracket;
- return 1;
- }
-
- if (json_parse_flags_allow_trailing_comma & flags_bitset) {
- allow_comma = 0;
- continue;
- } else {
- if (json_skip_all_skippables(state)) {
- state->error = json_parse_error_premature_end_of_buffer;
- return 1;
- }
- }
- }
-
- if (json_get_value_size(state, /* is_global_object = */ 0)) {
- /* value parsing failed! */
- return 1;
- }
-
- /* successfully parsed an array element! */
- elements++;
- allow_comma = 1;
- }
-
- /* we consumed the entire input before finding the closing ']' of the array!
- */
- state->error = json_parse_error_premature_end_of_buffer;
- return 1;
-}
-
-json_weak int json_get_number_size(struct json_parse_state_s *state);
- int json_get_number_size(struct json_parse_state_s *state) {
- const size_t flags_bitset = state->flags_bitset;
- size_t offset = state->offset;
- const size_t size = state->size;
- int had_leading_digits = 0;
- const char *const src = state->src;
-
- state->dom_size += sizeof(struct json_number_s);
-
- if ((json_parse_flags_allow_hexadecimal_numbers & flags_bitset) &&
- (offset + 1 < size) && ('0' == src[offset]) &&
- (('x' == src[offset + 1]) || ('X' == src[offset + 1]))) {
- /* skip the leading 0x that identifies a hexadecimal number. */
- offset += 2;
-
- /* consume hexadecimal digits. */
- while ((offset < size) && (('0' <= src[offset] && src[offset] <= '9') ||
- ('a' <= src[offset] && src[offset] <= 'f') ||
- ('A' <= src[offset] && src[offset] <= 'F'))) {
- offset++;
- }
- } else {
- int found_sign = 0;
- int inf_or_nan = 0;
-
- if ((offset < size) &&
- (('-' == src[offset]) ||
- ((json_parse_flags_allow_leading_plus_sign & flags_bitset) &&
- ('+' == src[offset])))) {
- /* skip valid leading '-' or '+'. */
- offset++;
-
- found_sign = 1;
- }
-
- if (json_parse_flags_allow_inf_and_nan & flags_bitset) {
- const char inf[9] = "Infinity";
- const size_t inf_strlen = sizeof(inf) - 1;
- const char nan[4] = "NaN";
- const size_t nan_strlen = sizeof(nan) - 1;
-
- if (offset + inf_strlen < size) {
- int found = 1;
- size_t i;
- for (i = 0; i < inf_strlen; i++) {
- if (inf[i] != src[offset + i]) {
- found = 0;
- break;
- }
- }
-
- if (found) {
- /* We found our special 'Infinity' keyword! */
- offset += inf_strlen;
-
- inf_or_nan = 1;
- }
- }
-
- if (offset + nan_strlen < size) {
- int found = 1;
- size_t i;
- for (i = 0; i < nan_strlen; i++) {
- if (nan[i] != src[offset + i]) {
- found = 0;
- break;
- }
- }
-
- if (found) {
- /* We found our special 'NaN' keyword! */
- offset += nan_strlen;
-
- inf_or_nan = 1;
- }
- }
- }
-
- if (found_sign && !inf_or_nan && (offset < size) &&
- !('0' <= src[offset] && src[offset] <= '9')) {
- /* check if we are allowing leading '.'. */
- if (!(json_parse_flags_allow_leading_or_trailing_decimal_point &
- flags_bitset) ||
- ('.' != src[offset])) {
- /* a leading '-' must be immediately followed by any digit! */
- state->error = json_parse_error_invalid_number_format;
- state->offset = offset;
- return 1;
- }
- }
-
- if ((offset < size) && ('0' == src[offset])) {
- /* skip valid '0'. */
- offset++;
-
- /* we need to record whether we had any leading digits for checks later.
- */
- had_leading_digits = 1;
-
- if ((offset < size) && ('0' <= src[offset] && src[offset] <= '9')) {
- /* a leading '0' must not be immediately followed by any digit! */
- state->error = json_parse_error_invalid_number_format;
- state->offset = offset;
- return 1;
- }
- }
-
- /* the main digits of our number next. */
- while ((offset < size) && ('0' <= src[offset] && src[offset] <= '9')) {
- offset++;
-
- /* we need to record whether we had any leading digits for checks later.
- */
- had_leading_digits = 1;
- }
-
- if ((offset < size) && ('.' == src[offset])) {
- offset++;
-
- if (!('0' <= src[offset] && src[offset] <= '9')) {
- if (!(json_parse_flags_allow_leading_or_trailing_decimal_point &
- flags_bitset) ||
- !had_leading_digits) {
- /* a decimal point must be followed by at least one digit. */
- state->error = json_parse_error_invalid_number_format;
- state->offset = offset;
- return 1;
- }
- }
-
- /* a decimal point can be followed by more digits of course! */
- while ((offset < size) && ('0' <= src[offset] && src[offset] <= '9')) {
- offset++;
- }
- }
-
- if ((offset < size) && ('e' == src[offset] || 'E' == src[offset])) {
- /* our number has an exponent! Wkip 'e' or 'E'. */
- offset++;
-
- if ((offset < size) && ('-' == src[offset] || '+' == src[offset])) {
- /* skip optional '-' or '+'. */
- offset++;
- }
-
- /* consume exponent digits. */
- while ((offset < size) && ('0' <= src[offset] && src[offset] <= '9')) {
- offset++;
- }
- }
- }
-
- if (offset < size) {
- switch (src[offset]) {
- case ' ':
- case '\t':
- case '\r':
- case '\n':
- case '}':
- case ',':
- case ']':
- /* all of the above are ok. */
- break;
- case '=':
- if (json_parse_flags_allow_equals_in_object & flags_bitset) {
- break;
- }
-
- state->error = json_parse_error_invalid_number_format;
- state->offset = offset;
- return 1;
- default:
- state->error = json_parse_error_invalid_number_format;
- state->offset = offset;
- return 1;
- }
- }
-
- state->data_size += offset - state->offset;
-
- /* one more byte for null terminator ending the number string! */
- state->data_size++;
-
- /* update offset. */
- state->offset = offset;
-
- return 0;
-}
-
-json_weak int json_get_value_size(struct json_parse_state_s *state,
- int is_global_object);
- int json_get_value_size(struct json_parse_state_s *state,
- int is_global_object) {
- const size_t flags_bitset = state->flags_bitset;
- const char *const src = state->src;
- size_t offset;
- const size_t size = state->size;
-
- if (json_parse_flags_allow_location_information & flags_bitset) {
- state->dom_size += sizeof(struct json_value_ex_s);
- } else {
- state->dom_size += sizeof(struct json_value_s);
- }
-
- if (is_global_object) {
- return json_get_object_size(state, /* is_global_object = */ 1);
- } else {
- if (json_skip_all_skippables(state)) {
- state->error = json_parse_error_premature_end_of_buffer;
- return 1;
- }
-
- /* can cache offset now. */
- offset = state->offset;
-
- switch (src[offset]) {
- case '"':
- return json_get_string_size(state, 0);
- case '\'':
- if (json_parse_flags_allow_single_quoted_strings & flags_bitset) {
- return json_get_string_size(state, 0);
- } else {
- /* invalid value! */
- state->error = json_parse_error_invalid_value;
- return 1;
- }
- case '{':
- return json_get_object_size(state, /* is_global_object = */ 0);
- case '[':
- return json_get_array_size(state);
- case '-':
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- return json_get_number_size(state);
- case '+':
- if (json_parse_flags_allow_leading_plus_sign & flags_bitset) {
- return json_get_number_size(state);
- } else {
- /* invalid value! */
- state->error = json_parse_error_invalid_number_format;
- return 1;
- }
- case '.':
- if (json_parse_flags_allow_leading_or_trailing_decimal_point &
- flags_bitset) {
- return json_get_number_size(state);
- } else {
- /* invalid value! */
- state->error = json_parse_error_invalid_number_format;
- return 1;
- }
- default:
- if ((offset + 4) <= size && 't' == src[offset + 0] &&
- 'r' == src[offset + 1] && 'u' == src[offset + 2] &&
- 'e' == src[offset + 3]) {
- state->offset += 4;
- return 0;
- } else if ((offset + 5) <= size && 'f' == src[offset + 0] &&
- 'a' == src[offset + 1] && 'l' == src[offset + 2] &&
- 's' == src[offset + 3] && 'e' == src[offset + 4]) {
- state->offset += 5;
- return 0;
- } else if ((offset + 4) <= size && 'n' == state->src[offset + 0] &&
- 'u' == state->src[offset + 1] &&
- 'l' == state->src[offset + 2] &&
- 'l' == state->src[offset + 3]) {
- state->offset += 4;
- return 0;
- } else if ((json_parse_flags_allow_inf_and_nan & flags_bitset) &&
- (offset + 3) <= size && 'N' == src[offset + 0] &&
- 'a' == src[offset + 1] && 'N' == src[offset + 2]) {
- return json_get_number_size(state);
- } else if ((json_parse_flags_allow_inf_and_nan & flags_bitset) &&
- (offset + 8) <= size && 'I' == src[offset + 0] &&
- 'n' == src[offset + 1] && 'f' == src[offset + 2] &&
- 'i' == src[offset + 3] && 'n' == src[offset + 4] &&
- 'i' == src[offset + 5] && 't' == src[offset + 6] &&
- 'y' == src[offset + 7]) {
- return json_get_number_size(state);
- }
-
- /* invalid value! */
- state->error = json_parse_error_invalid_value;
- return 1;
- }
- }
-}
-
-json_weak void json_parse_value(struct json_parse_state_s *state,
- int is_global_object, struct json_value_s *value);
-
-json_weak void json_parse_string(struct json_parse_state_s *state,
- struct json_string_s *string);
- void json_parse_string(struct json_parse_state_s *state,
- struct json_string_s *string) {
- size_t offset = state->offset;
- size_t bytes_written = 0;
- const char *const src = state->src;
- const char quote_to_use = '\'' == src[offset] ? '\'' : '"';
- char *data = state->data;
- unsigned long high_surrogate = 0;
- unsigned long codepoint;
-
- string->string = data;
-
- /* skip leading '"' or '\''. */
- offset++;
-
- while (quote_to_use != src[offset]) {
- if ('\\' == src[offset]) {
- /* skip the reverse solidus. */
- offset++;
-
- switch (src[offset++]) {
- default:
- return; /* we cannot ever reach here. */
- case 'u': {
- codepoint = 0;
- if (!json_hexadecimal_value(&src[offset], 4, &codepoint)) {
- return; /* this shouldn't happen as the value was already validated.
- */
- }
-
- offset += 4;
-
- if (codepoint <= 0x7fu) {
- data[bytes_written++] = (char)codepoint; /* 0xxxxxxx. */
- } else if (codepoint <= 0x7ffu) {
- data[bytes_written++] =
- (char)(0xc0u | (codepoint >> 6)); /* 110xxxxx. */
- data[bytes_written++] =
- (char)(0x80u | (codepoint & 0x3fu)); /* 10xxxxxx. */
- } else if (codepoint >= 0xd800 &&
- codepoint <= 0xdbff) { /* high surrogate. */
- high_surrogate = codepoint;
- continue; /* we need the low half to form a complete codepoint. */
- } else if (codepoint >= 0xdc00 &&
- codepoint <= 0xdfff) { /* low surrogate. */
- /* combine with the previously read half to obtain the complete
- * codepoint. */
- const unsigned long surrogate_offset = 0x10000u - (0xD800u << 10) - 0xDC00u;
- codepoint = (high_surrogate << 10) + codepoint + surrogate_offset;
- high_surrogate = 0;
- data[bytes_written++] =
- (char)(0xF0u | (codepoint >> 18)); /* 11110xxx. */
- data[bytes_written++] =
- (char)(0x80u | ((codepoint >> 12) & 0x3fu)); /* 10xxxxxx. */
- data[bytes_written++] =
- (char)(0x80u | ((codepoint >> 6) & 0x3fu)); /* 10xxxxxx. */
- data[bytes_written++] =
- (char)(0x80u | (codepoint & 0x3fu)); /* 10xxxxxx. */
- } else {
- /* we assume the value was validated and thus is within the valid
- * range. */
- data[bytes_written++] =
- (char)(0xe0u | (codepoint >> 12)); /* 1110xxxx. */
- data[bytes_written++] =
- (char)(0x80u | ((codepoint >> 6) & 0x3fu)); /* 10xxxxxx. */
- data[bytes_written++] =
- (char)(0x80u | (codepoint & 0x3fu)); /* 10xxxxxx. */
- }
- } break;
- case '"':
- data[bytes_written++] = '"';
- break;
- case '\\':
- data[bytes_written++] = '\\';
- break;
- case '/':
- data[bytes_written++] = '/';
- break;
- case 'b':
- data[bytes_written++] = '\b';
- break;
- case 'f':
- data[bytes_written++] = '\f';
- break;
- case 'n':
- data[bytes_written++] = '\n';
- break;
- case 'r':
- data[bytes_written++] = '\r';
- break;
- case 't':
- data[bytes_written++] = '\t';
- break;
- case '\r':
- data[bytes_written++] = '\r';
-
- /* check if we have a "\r\n" sequence. */
- if ('\n' == src[offset]) {
- data[bytes_written++] = '\n';
- offset++;
- }
-
- break;
- case '\n':
- data[bytes_written++] = '\n';
- break;
- }
- } else {
- /* copy the character. */
- data[bytes_written++] = src[offset++];
- }
- }
-
- /* skip trailing '"' or '\''. */
- offset++;
-
- /* record the size of the string. */
- string->string_size = bytes_written;
-
- /* add null terminator to string. */
- data[bytes_written++] = '\0';
-
- /* move data along. */
- state->data += bytes_written;
-
- /* update offset. */
- state->offset = offset;
-}
-
-json_weak void json_parse_key(struct json_parse_state_s *state,
- struct json_string_s *string);
- void json_parse_key(struct json_parse_state_s *state,
- struct json_string_s *string) {
- if (json_parse_flags_allow_unquoted_keys & state->flags_bitset) {
- const char *const src = state->src;
- char *const data = state->data;
- size_t offset = state->offset;
-
- /* if we are allowing unquoted keys, check for quoted anyway... */
- if (('"' == src[offset]) || ('\'' == src[offset])) {
- /* ... if we got a quote, just parse the key as a string as normal. */
- json_parse_string(state, string);
- } else {
- size_t size = 0;
-
- string->string = state->data;
-
- while (is_valid_unquoted_key_char(src[offset])) {
- data[size++] = src[offset++];
- }
-
- /* add null terminator to string. */
- data[size] = '\0';
-
- /* record the size of the string. */
- string->string_size = size++;
-
- /* move data along. */
- state->data += size;
-
- /* update offset. */
- state->offset = offset;
- }
- } else {
- /* we are only allowed to have quoted keys, so just parse a string! */
- json_parse_string(state, string);
- }
-}
-
-json_weak void json_parse_object(struct json_parse_state_s *state,
- int is_global_object,
- struct json_object_s *object);
- void json_parse_object(struct json_parse_state_s *state,
- int is_global_object,
- struct json_object_s *object) {
- const size_t flags_bitset = state->flags_bitset;
- const size_t size = state->size;
- const char *const src = state->src;
- size_t elements = 0;
- int allow_comma = 0;
- struct json_object_element_s *previous = json_null;
-
- if (is_global_object) {
- /* if we skipped some whitespace, and then found an opening '{' of an. */
- /* object, we actually have a normal JSON object at the root of the DOM...
- */
- if ('{' == src[state->offset]) {
- /* . and we don't actually have a global object after all! */
- is_global_object = 0;
- }
- }
-
- if (!is_global_object) {
- /* skip leading '{'. */
- state->offset++;
- }
-
- (void)json_skip_all_skippables(state);
-
- /* reset elements. */
- elements = 0;
-
- while (state->offset < size) {
- struct json_object_element_s *element = json_null;
- struct json_string_s *string = json_null;
- struct json_value_s *value = json_null;
-
- if (!is_global_object) {
- (void)json_skip_all_skippables(state);
-
- if ('}' == src[state->offset]) {
- /* skip trailing '}'. */
- state->offset++;
-
- /* finished the object! */
- break;
- }
- } else {
- if (json_skip_all_skippables(state)) {
- /* global object ends when the file ends! */
- break;
- }
- }
-
- /* if we parsed at least one element previously, grok for a comma. */
- if (allow_comma) {
- if (',' == src[state->offset]) {
- /* skip comma. */
- state->offset++;
- allow_comma = 0;
- continue;
- }
- }
-
- element = (struct json_object_element_s *)state->dom;
-
- state->dom += sizeof(struct json_object_element_s);
-
- if (json_null == previous) {
- /* this is our first element, so record it in our object. */
- object->start = element;
- } else {
- previous->next = element;
- }
-
- previous = element;
-
- if (json_parse_flags_allow_location_information & flags_bitset) {
- struct json_string_ex_s *string_ex =
- (struct json_string_ex_s *)state->dom;
- state->dom += sizeof(struct json_string_ex_s);
-
- string_ex->offset = state->offset;
- string_ex->line_no = state->line_no;
- string_ex->row_no = state->offset - state->line_offset;
-
- string = &(string_ex->string);
- } else {
- string = (struct json_string_s *)state->dom;
- state->dom += sizeof(struct json_string_s);
- }
-
- element->name = string;
-
- (void)json_parse_key(state, string);
-
- (void)json_skip_all_skippables(state);
-
- /* skip colon or equals. */
- state->offset++;
-
- (void)json_skip_all_skippables(state);
-
- if (json_parse_flags_allow_location_information & flags_bitset) {
- struct json_value_ex_s *value_ex = (struct json_value_ex_s *)state->dom;
- state->dom += sizeof(struct json_value_ex_s);
-
- value_ex->offset = state->offset;
- value_ex->line_no = state->line_no;
- value_ex->row_no = state->offset - state->line_offset;
-
- value = &(value_ex->value);
- } else {
- value = (struct json_value_s *)state->dom;
- state->dom += sizeof(struct json_value_s);
- }
-
- element->value = value;
-
- json_parse_value(state, /* is_global_object = */ 0, value);
-
- /* successfully parsed a name/value pair! */
- elements++;
- allow_comma = 1;
- }
-
- /* if we had at least one element, end the linked list. */
- if (previous) {
- previous->next = json_null;
- }
-
- if (0 == elements) {
- object->start = json_null;
- }
-
- object->length = elements;
-}
-
-json_weak void json_parse_array(struct json_parse_state_s *state,
- struct json_array_s *array);
- void json_parse_array(struct json_parse_state_s *state,
- struct json_array_s *array) {
- const char *const src = state->src;
- const size_t size = state->size;
- size_t elements = 0;
- int allow_comma = 0;
- struct json_array_element_s *previous = json_null;
-
- /* skip leading '['. */
- state->offset++;
-
- (void)json_skip_all_skippables(state);
-
- /* reset elements. */
- elements = 0;
-
- do {
- struct json_array_element_s *element = json_null;
- struct json_value_s *value = json_null;
-
- (void)json_skip_all_skippables(state);
-
- if (']' == src[state->offset]) {
- /* skip trailing ']'. */
- state->offset++;
-
- /* finished the array! */
- break;
- }
-
- /* if we parsed at least one element previously, grok for a comma. */
- if (allow_comma) {
- if (',' == src[state->offset]) {
- /* skip comma. */
- state->offset++;
- allow_comma = 0;
- continue;
- }
- }
-
- element = (struct json_array_element_s *)state->dom;
-
- state->dom += sizeof(struct json_array_element_s);
-
- if (json_null == previous) {
- /* this is our first element, so record it in our array. */
- array->start = element;
- } else {
- previous->next = element;
- }
-
- previous = element;
-
- if (json_parse_flags_allow_location_information & state->flags_bitset) {
- struct json_value_ex_s *value_ex = (struct json_value_ex_s *)state->dom;
- state->dom += sizeof(struct json_value_ex_s);
-
- value_ex->offset = state->offset;
- value_ex->line_no = state->line_no;
- value_ex->row_no = state->offset - state->line_offset;
-
- value = &(value_ex->value);
- } else {
- value = (struct json_value_s *)state->dom;
- state->dom += sizeof(struct json_value_s);
- }
-
- element->value = value;
-
- json_parse_value(state, /* is_global_object = */ 0, value);
-
- /* successfully parsed an array element! */
- elements++;
- allow_comma = 1;
- } while (state->offset < size);
-
- /* end the linked list. */
- if (previous) {
- previous->next = json_null;
- }
-
- if (0 == elements) {
- array->start = json_null;
- }
-
- array->length = elements;
-}
-
-json_weak void json_parse_number(struct json_parse_state_s *state,
- struct json_number_s *number);
- void json_parse_number(struct json_parse_state_s *state,
- struct json_number_s *number) {
- const size_t flags_bitset = state->flags_bitset;
- size_t offset = state->offset;
- const size_t size = state->size;
- size_t bytes_written = 0;
- const char *const src = state->src;
- char *data = state->data;
-
- number->number = data;
-
- if (json_parse_flags_allow_hexadecimal_numbers & flags_bitset) {
- if (('0' == src[offset]) &&
- (('x' == src[offset + 1]) || ('X' == src[offset + 1]))) {
- /* consume hexadecimal digits. */
- while ((offset < size) &&
- (('0' <= src[offset] && src[offset] <= '9') ||
- ('a' <= src[offset] && src[offset] <= 'f') ||
- ('A' <= src[offset] && src[offset] <= 'F') ||
- ('x' == src[offset]) || ('X' == src[offset]))) {
- data[bytes_written++] = src[offset++];
- }
- }
- }
-
- while (offset < size) {
- int end = 0;
-
- switch (src[offset]) {
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- case '.':
- case 'e':
- case 'E':
- case '+':
- case '-':
- data[bytes_written++] = src[offset++];
- break;
- default:
- end = 1;
- break;
- }
-
- if (0 != end) {
- break;
- }
- }
-
- if (json_parse_flags_allow_inf_and_nan & flags_bitset) {
- const size_t inf_strlen = 8; /* = strlen("Infinity");. */
- const size_t nan_strlen = 3; /* = strlen("NaN");. */
-
- if (offset + inf_strlen < size) {
- if ('I' == src[offset]) {
- size_t i;
- /* We found our special 'Infinity' keyword! */
- for (i = 0; i < inf_strlen; i++) {
- data[bytes_written++] = src[offset++];
- }
- }
- }
-
- if (offset + nan_strlen < size) {
- if ('N' == src[offset]) {
- size_t i;
- /* We found our special 'NaN' keyword! */
- for (i = 0; i < nan_strlen; i++) {
- data[bytes_written++] = src[offset++];
- }
- }
- }
- }
-
- /* record the size of the number. */
- number->number_size = bytes_written;
- /* add null terminator to number string. */
- data[bytes_written++] = '\0';
- /* move data along. */
- state->data += bytes_written;
- /* update offset. */
- state->offset = offset;
-}
-
-json_weak void json_parse_value(struct json_parse_state_s *state,
- int is_global_object, struct json_value_s *value);
- void json_parse_value(struct json_parse_state_s *state,
- int is_global_object, struct json_value_s *value) {
- const size_t flags_bitset = state->flags_bitset;
- const char *const src = state->src;
- const size_t size = state->size;
- size_t offset;
-
- (void)json_skip_all_skippables(state);
-
- /* cache offset now. */
- offset = state->offset;
-
- if (is_global_object) {
- value->type = json_type_object;
- value->payload = state->dom;
- state->dom += sizeof(struct json_object_s);
- json_parse_object(state, /* is_global_object = */ 1,
- (struct json_object_s *)value->payload);
- } else {
- switch (src[offset]) {
- case '"':
- case '\'':
- value->type = json_type_string;
- value->payload = state->dom;
- state->dom += sizeof(struct json_string_s);
- json_parse_string(state, (struct json_string_s *)value->payload);
- break;
- case '{':
- value->type = json_type_object;
- value->payload = state->dom;
- state->dom += sizeof(struct json_object_s);
- json_parse_object(state, /* is_global_object = */ 0,
- (struct json_object_s *)value->payload);
- break;
- case '[':
- value->type = json_type_array;
- value->payload = state->dom;
- state->dom += sizeof(struct json_array_s);
- json_parse_array(state, (struct json_array_s *)value->payload);
- break;
- case '-':
- case '+':
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- case '8':
- case '9':
- case '.':
- value->type = json_type_number;
- value->payload = state->dom;
- state->dom += sizeof(struct json_number_s);
- json_parse_number(state, (struct json_number_s *)value->payload);
- break;
- default:
- if ((offset + 4) <= size && 't' == src[offset + 0] &&
- 'r' == src[offset + 1] && 'u' == src[offset + 2] &&
- 'e' == src[offset + 3]) {
- value->type = json_type_true;
- value->payload = json_null;
- state->offset += 4;
- } else if ((offset + 5) <= size && 'f' == src[offset + 0] &&
- 'a' == src[offset + 1] && 'l' == src[offset + 2] &&
- 's' == src[offset + 3] && 'e' == src[offset + 4]) {
- value->type = json_type_false;
- value->payload = json_null;
- state->offset += 5;
- } else if ((offset + 4) <= size && 'n' == src[offset + 0] &&
- 'u' == src[offset + 1] && 'l' == src[offset + 2] &&
- 'l' == src[offset + 3]) {
- value->type = json_type_null;
- value->payload = json_null;
- state->offset += 4;
- } else if ((json_parse_flags_allow_inf_and_nan & flags_bitset) &&
- (offset + 3) <= size && 'N' == src[offset + 0] &&
- 'a' == src[offset + 1] && 'N' == src[offset + 2]) {
- value->type = json_type_number;
- value->payload = state->dom;
- state->dom += sizeof(struct json_number_s);
- json_parse_number(state, (struct json_number_s *)value->payload);
- } else if ((json_parse_flags_allow_inf_and_nan & flags_bitset) &&
- (offset + 8) <= size && 'I' == src[offset + 0] &&
- 'n' == src[offset + 1] && 'f' == src[offset + 2] &&
- 'i' == src[offset + 3] && 'n' == src[offset + 4] &&
- 'i' == src[offset + 5] && 't' == src[offset + 6] &&
- 'y' == src[offset + 7]) {
- value->type = json_type_number;
- value->payload = state->dom;
- state->dom += sizeof(struct json_number_s);
- json_parse_number(state, (struct json_number_s *)value->payload);
- }
- break;
- }
- }
-}
-
-struct json_value_s *
-json_parse_ex(const void *src, size_t src_size, size_t flags_bitset,
- void *(*alloc_func_ptr)(void *user_data, size_t size),
- void *user_data, struct json_parse_result_s *result) {
- struct json_parse_state_s state;
- void *allocation;
- struct json_value_s *value;
- size_t total_size;
- int input_error;
-
- if (result) {
- result->error = json_parse_error_none;
- result->error_offset = 0;
- result->error_line_no = 0;
- result->error_row_no = 0;
- }
-
- if (json_null == src) {
- /* invalid src pointer was null! */
- return json_null;
- }
-
- state.src = (const char *)src;
- state.size = src_size;
- state.offset = 0;
- state.line_no = 1;
- state.line_offset = 0;
- state.error = json_parse_error_none;
- state.dom_size = 0;
- state.data_size = 0;
- state.flags_bitset = flags_bitset;
-
- input_error = json_get_value_size(
- &state, (int)(json_parse_flags_allow_global_object & state.flags_bitset));
-
- if (0 == input_error) {
- json_skip_all_skippables(&state);
-
- if (state.offset != state.size) {
- /* our parsing didn't have an error, but there are characters remaining in
- * the input that weren't part of the JSON! */
-
- state.error = json_parse_error_unexpected_trailing_characters;
- input_error = 1;
- }
- }
-
- if (input_error) {
- /* parsing value's size failed (most likely an invalid JSON DOM!). */
- if (result) {
- result->error = state.error;
- result->error_offset = state.offset;
- result->error_line_no = state.line_no;
- result->error_row_no = state.offset - state.line_offset;
- }
- return json_null;
- }
-
- /* our total allocation is the combination of the dom and data sizes (we. */
- /* first encode the structure of the JSON, and then the data referenced by. */
- /* the JSON values). */
- total_size = state.dom_size + state.data_size;
-
- if (json_null == alloc_func_ptr) {
- allocation = malloc(total_size);
- } else {
- allocation = alloc_func_ptr(user_data, total_size);
- }
-
- if (json_null == allocation) {
- /* malloc failed! */
- if (result) {
- result->error = json_parse_error_allocator_failed;
- result->error_offset = 0;
- result->error_line_no = 0;
- result->error_row_no = 0;
- }
-
- return json_null;
- }
-
- /* reset offset so we can reuse it. */
- state.offset = 0;
-
- /* reset the line information so we can reuse it. */
- state.line_no = 1;
- state.line_offset = 0;
-
- state.dom = (char *)allocation;
- state.data = state.dom + state.dom_size;
-
- if (json_parse_flags_allow_location_information & state.flags_bitset) {
- struct json_value_ex_s *value_ex = (struct json_value_ex_s *)state.dom;
- state.dom += sizeof(struct json_value_ex_s);
-
- value_ex->offset = state.offset;
- value_ex->line_no = state.line_no;
- value_ex->row_no = state.offset - state.line_offset;
-
- value = &(value_ex->value);
- } else {
- value = (struct json_value_s *)state.dom;
- state.dom += sizeof(struct json_value_s);
- }
-
- json_parse_value(
- &state, (int)(json_parse_flags_allow_global_object & state.flags_bitset),
- value);
-
- return (struct json_value_s *)allocation;
-}
-
-struct json_value_s *json_parse(const void *src, size_t src_size) {
- return json_parse_ex(src, src_size, json_parse_flags_default, json_null,
- json_null, json_null);
-}
-
-struct json_string_s *json_value_as_string(struct json_value_s *const value) {
- if (value->type != json_type_string) {
- return json_null;
- }
-
- return (struct json_string_s *)value->payload;
-}
-
-struct json_number_s *json_value_as_number(struct json_value_s *const value) {
- if (value->type != json_type_number) {
- return json_null;
- }
-
- return (struct json_number_s *)value->payload;
-}
-
-struct json_object_s *json_value_as_object(struct json_value_s *const value) {
- if (value->type != json_type_object) {
- return json_null;
- }
-
- return (struct json_object_s *)value->payload;
-}
-
-struct json_array_s *json_value_as_array(struct json_value_s *const value) {
- if (value->type != json_type_array) {
- return json_null;
- }
-
- return (struct json_array_s *)value->payload;
-}
-
-int json_value_is_true(const struct json_value_s *const value) {
- return value->type == json_type_true;
-}
-
-int json_value_is_false(const struct json_value_s *const value) {
- return value->type == json_type_false;
-}
-
-int json_value_is_null(const struct json_value_s *const value) {
- return value->type == json_type_null;
-}
-
-json_weak int json_write_minified_get_value_size(const struct json_value_s *value,
- size_t *size);
-
-json_weak int json_write_get_number_size(const struct json_number_s *number,
- size_t *size);
- int json_write_get_number_size(const struct json_number_s *number,
- size_t *size) {
- json_uintmax_t parsed_number;
- size_t i;
-
- if (number->number_size >= 2) {
- switch (number->number[1]) {
- default:
- break;
- case 'x':
- case 'X':
- /* the number is a json_parse_flags_allow_hexadecimal_numbers hexadecimal
- * so we have to do extra work to convert it to a non-hexadecimal for JSON
- * output. */
- parsed_number = json_strtoumax(number->number, json_null, 0);
-
- i = 0;
-
- while (0 != parsed_number) {
- parsed_number /= 10;
- i++;
- }
-
- *size += i;
- return 0;
- }
- }
-
- /* check to see if the number has leading/trailing decimal point. */
- i = 0;
-
- /* skip any leading '+' or '-'. */
- if ((i < number->number_size) &&
- (('+' == number->number[i]) || ('-' == number->number[i]))) {
- i++;
- }
-
- /* check if we have infinity. */
- if ((i < number->number_size) && ('I' == number->number[i])) {
- const char *inf = "Infinity";
- size_t k;
-
- for (k = i; k < number->number_size; k++) {
- const char c = *inf++;
-
- /* Check if we found the Infinity string! */
- if ('\0' == c) {
- break;
- } else if (c != number->number[k]) {
- break;
- }
- }
-
- if ('\0' == *inf) {
- /* Inf becomes 1.7976931348623158e308 because JSON can't support it. */
- *size += 22;
-
- /* if we had a leading '-' we need to record it in the JSON output. */
- if ('-' == number->number[0]) {
- *size += 1;
- }
- }
-
- return 0;
- }
-
- /* check if we have nan. */
- if ((i < number->number_size) && ('N' == number->number[i])) {
- const char *nan = "NaN";
- size_t k;
-
- for (k = i; k < number->number_size; k++) {
- const char c = *nan++;
-
- /* Check if we found the NaN string! */
- if ('\0' == c) {
- break;
- } else if (c != number->number[k]) {
- break;
- }
- }
-
- if ('\0' == *nan) {
- /* NaN becomes 1 because JSON can't support it. */
- *size += 1;
-
- return 0;
- }
- }
-
- /* if we had a leading decimal point. */
- if ((i < number->number_size) && ('.' == number->number[i])) {
- /* 1 + because we had a leading decimal point. */
- *size += 1;
- goto cleanup;
- }
-
- for (; i < number->number_size; i++) {
- const char c = number->number[i];
- if (!('0' <= c && c <= '9')) {
- break;
- }
- }
-
- /* if we had a trailing decimal point. */
- if ((i + 1 == number->number_size) && ('.' == number->number[i])) {
- /* 1 + because we had a trailing decimal point. */
- *size += 1;
- goto cleanup;
- }
-
-cleanup:
- *size += number->number_size; /* the actual string of the number. */
-
- /* if we had a leading '+' we don't record it in the JSON output. */
- if ('+' == number->number[0]) {
- *size -= 1;
- }
-
- return 0;
-}
-
-json_weak int json_write_get_string_size(const struct json_string_s *string,
- size_t *size);
- int json_write_get_string_size(const struct json_string_s *string,
- size_t *size) {
- size_t i;
- for (i = 0; i < string->string_size; i++) {
- switch (string->string[i]) {
- case '"':
- case '\\':
- case '\b':
- case '\f':
- case '\n':
- case '\r':
- case '\t':
- *size += 2;
- break;
- default:
- *size += 1;
- break;
- }
- }
-
- *size += 2; /* need to encode the surrounding '"' characters. */
-
- return 0;
-}
-
-json_weak int json_write_minified_get_array_size(const struct json_array_s *array,
- size_t *size);
- int json_write_minified_get_array_size(const struct json_array_s *array,
- size_t *size) {
- struct json_array_element_s *element;
-
- *size += 2; /* '[' and ']'. */
-
- if (1 < array->length) {
- *size += array->length - 1; /* ','s seperate each element. */
- }
-
- for (element = array->start; json_null != element; element = element->next) {
- if (json_write_minified_get_value_size(element->value, size)) {
- /* value was malformed! */
- return 1;
- }
- }
-
- return 0;
-}
-
-json_weak int
-json_write_minified_get_object_size(const struct json_object_s *object,
- size_t *size);
- int
-json_write_minified_get_object_size(const struct json_object_s *object,
- size_t *size) {
- struct json_object_element_s *element;
-
- *size += 2; /* '{' and '}'. */
-
- *size += object->length; /* ':'s seperate each name/value pair. */
-
- if (1 < object->length) {
- *size += object->length - 1; /* ','s seperate each element. */
- }
-
- for (element = object->start; json_null != element; element = element->next) {
- if (json_write_get_string_size(element->name, size)) {
- /* string was malformed! */
- return 1;
- }
-
- if (json_write_minified_get_value_size(element->value, size)) {
- /* value was malformed! */
- return 1;
- }
- }
-
- return 0;
-}
-
-json_weak int json_write_minified_get_value_size(const struct json_value_s *value,
- size_t *size);
- int json_write_minified_get_value_size(const struct json_value_s *value,
- size_t *size) {
- switch (value->type) {
- default:
- /* unknown value type found! */
- return 1;
- case json_type_number:
- return json_write_get_number_size((struct json_number_s *)value->payload,
- size);
- case json_type_string:
- return json_write_get_string_size((struct json_string_s *)value->payload,
- size);
- case json_type_array:
- return json_write_minified_get_array_size(
- (struct json_array_s *)value->payload, size);
- case json_type_object:
- return json_write_minified_get_object_size(
- (struct json_object_s *)value->payload, size);
- case json_type_true:
- *size += 4; /* the string "true". */
- return 0;
- case json_type_false:
- *size += 5; /* the string "false". */
- return 0;
- case json_type_null:
- *size += 4; /* the string "null". */
- return 0;
- }
-}
-
-json_weak char *json_write_minified_value(const struct json_value_s *value,
- char *data);
-
-json_weak char *json_write_number(const struct json_number_s *number, char *data);
- char *json_write_number(const struct json_number_s *number, char *data) {
- json_uintmax_t parsed_number, backup;
- size_t i;
-
- if (number->number_size >= 2) {
- switch (number->number[1]) {
- default:
- break;
- case 'x':
- case 'X':
- /* The number is a json_parse_flags_allow_hexadecimal_numbers hexadecimal
- * so we have to do extra work to convert it to a non-hexadecimal for JSON
- * output. */
- parsed_number = json_strtoumax(number->number, json_null, 0);
-
- /* We need a copy of parsed number twice, so take a backup of it. */
- backup = parsed_number;
-
- i = 0;
-
- while (0 != parsed_number) {
- parsed_number /= 10;
- i++;
- }
-
- /* Restore parsed_number to its original value stored in the backup. */
- parsed_number = backup;
-
- /* Now use backup to take a copy of i, or the length of the string. */
- backup = i;
-
- do {
- *(data + i - 1) = '0' + (char)(parsed_number % 10);
- parsed_number /= 10;
- i--;
- } while (0 != parsed_number);
-
- data += backup;
-
- return data;
- }
- }
-
- /* check to see if the number has leading/trailing decimal point. */
- i = 0;
-
- /* skip any leading '-'. */
- if ((i < number->number_size) &&
- (('+' == number->number[i]) || ('-' == number->number[i]))) {
- i++;
- }
-
- /* check if we have infinity. */
- if ((i < number->number_size) && ('I' == number->number[i])) {
- const char *inf = "Infinity";
- size_t k;
-
- for (k = i; k < number->number_size; k++) {
- const char c = *inf++;
-
- /* Check if we found the Infinity string! */
- if ('\0' == c) {
- break;
- } else if (c != number->number[k]) {
- break;
- }
- }
-
- if ('\0' == *inf++) {
- const char *dbl_max;
-
- /* if we had a leading '-' we need to record it in the JSON output. */
- if ('-' == number->number[0]) {
- *data++ = '-';
- }
-
- /* Inf becomes 1.7976931348623158e308 because JSON can't support it. */
- for (dbl_max = "1.7976931348623158e308"; '\0' != *dbl_max; dbl_max++) {
- *data++ = *dbl_max;
- }
-
- return data;
- }
- }
-
- /* check if we have nan. */
- if ((i < number->number_size) && ('N' == number->number[i])) {
- const char *nan = "NaN";
- size_t k;
-
- for (k = i; k < number->number_size; k++) {
- const char c = *nan++;
-
- /* Check if we found the NaN string! */
- if ('\0' == c) {
- break;
- } else if (c != number->number[k]) {
- break;
- }
- }
-
- if ('\0' == *nan++) {
- /* NaN becomes 0 because JSON can't support it. */
- *data++ = '0';
- return data;
- }
- }
-
- /* if we had a leading decimal point. */
- if ((i < number->number_size) && ('.' == number->number[i])) {
- i = 0;
-
- /* skip any leading '+'. */
- if ('+' == number->number[i]) {
- i++;
- }
-
- /* output the leading '-' if we had one. */
- if ('-' == number->number[i]) {
- *data++ = '-';
- i++;
- }
-
- /* insert a '0' to fix the leading decimal point for JSON output. */
- *data++ = '0';
-
- /* and output the rest of the number as normal. */
- for (; i < number->number_size; i++) {
- *data++ = number->number[i];
- }
-
- return data;
- }
-
- for (; i < number->number_size; i++) {
- const char c = number->number[i];
- if (!('0' <= c && c <= '9')) {
- break;
- }
- }
-
- /* if we had a trailing decimal point. */
- if ((i + 1 == number->number_size) && ('.' == number->number[i])) {
- i = 0;
-
- /* skip any leading '+'. */
- if ('+' == number->number[i]) {
- i++;
- }
-
- /* output the leading '-' if we had one. */
- if ('-' == number->number[i]) {
- *data++ = '-';
- i++;
- }
-
- /* and output the rest of the number as normal. */
- for (; i < number->number_size; i++) {
- *data++ = number->number[i];
- }
-
- /* insert a '0' to fix the trailing decimal point for JSON output. */
- *data++ = '0';
-
- return data;
- }
-
- i = 0;
-
- /* skip any leading '+'. */
- if ('+' == number->number[i]) {
- i++;
- }
-
- for (; i < number->number_size; i++) {
- *data++ = number->number[i];
- }
-
- return data;
-}
-
-json_weak char *json_write_string(const struct json_string_s *string, char *data);
- char *json_write_string(const struct json_string_s *string, char *data) {
- size_t i;
-
- *data++ = '"'; /* open the string. */
-
- for (i = 0; i < string->string_size; i++) {
- switch (string->string[i]) {
- case '"':
- *data++ = '\\'; /* escape the control character. */
- *data++ = '"';
- break;
- case '\\':
- *data++ = '\\'; /* escape the control character. */
- *data++ = '\\';
- break;
- case '\b':
- *data++ = '\\'; /* escape the control character. */
- *data++ = 'b';
- break;
- case '\f':
- *data++ = '\\'; /* escape the control character. */
- *data++ = 'f';
- break;
- case '\n':
- *data++ = '\\'; /* escape the control character. */
- *data++ = 'n';
- break;
- case '\r':
- *data++ = '\\'; /* escape the control character. */
- *data++ = 'r';
- break;
- case '\t':
- *data++ = '\\'; /* escape the control character. */
- *data++ = 't';
- break;
- default:
- *data++ = string->string[i];
- break;
- }
- }
-
- *data++ = '"'; /* close the string. */
-
- return data;
-}
-
-json_weak char *json_write_minified_array(const struct json_array_s *array,
- char *data);
-char *json_write_minified_array(const struct json_array_s *array,
- char *data) {
- struct json_array_element_s *element = json_null;
-
- *data++ = '['; /* open the array. */
-
- for (element = array->start; json_null != element; element = element->next) {
- if (element != array->start) {
- *data++ = ','; /* ','s seperate each element. */
- }
-
- data = json_write_minified_value(element->value, data);
-
- if (json_null == data) {
- /* value was malformed! */
- return json_null;
- }
- }
-
- *data++ = ']'; /* close the array. */
-
- return data;
-}
-
-json_weak char *json_write_minified_object(const struct json_object_s *object,
- char *data);
- char *json_write_minified_object(const struct json_object_s *object,
- char *data) {
- struct json_object_element_s *element = json_null;
-
- *data++ = '{'; /* open the object. */
-
- for (element = object->start; json_null != element;
- element = element->next) {
- if (element != object->start) {
- *data++ = ','; /* ','s seperate each element. */
- }
-
- data = json_write_string(element->name, data);
-
- if (json_null == data) {
- /* string was malformed! */
- return json_null;
- }
-
- *data++ = ':'; /* ':'s seperate each name/value pair. */
-
- data = json_write_minified_value(element->value, data);
-
- if (json_null == data) {
- /* value was malformed! */
- return json_null;
- }
- }
-
- *data++ = '}'; /* close the object. */
-
- return data;
-}
-
-json_weak char *json_write_minified_value(const struct json_value_s *value,
- char *data);
- char *json_write_minified_value(const struct json_value_s *value,
- char *data) {
- switch (value->type) {
- default:
- /* unknown value type found! */
- return json_null;
- case json_type_number:
- return json_write_number((struct json_number_s *)value->payload, data);
- case json_type_string:
- return json_write_string((struct json_string_s *)value->payload, data);
- case json_type_array:
- return json_write_minified_array((struct json_array_s *)value->payload,
- data);
- case json_type_object:
- return json_write_minified_object((struct json_object_s *)value->payload,
- data);
- case json_type_true:
- data[0] = 't';
- data[1] = 'r';
- data[2] = 'u';
- data[3] = 'e';
- return data + 4;
- case json_type_false:
- data[0] = 'f';
- data[1] = 'a';
- data[2] = 'l';
- data[3] = 's';
- data[4] = 'e';
- return data + 5;
- case json_type_null:
- data[0] = 'n';
- data[1] = 'u';
- data[2] = 'l';
- data[3] = 'l';
- return data + 4;
- }
-}
-
-void *json_write_minified(const struct json_value_s *value, size_t *out_size) {
- size_t size = 0;
- char *data = json_null;
- char *data_end = json_null;
-
- if (json_null == value) {
- return json_null;
- }
-
- if (json_write_minified_get_value_size(value, &size)) {
- /* value was malformed! */
- return json_null;
- }
-
- size += 1; /* for the '\0' null terminating character. */
-
- data = (char *)malloc(size);
-
- if (json_null == data) {
- /* malloc failed! */
- return json_null;
- }
-
- data_end = json_write_minified_value(value, data);
-
- if (json_null == data_end) {
- /* bad chi occurred! */
- free(data);
- return json_null;
- }
-
- /* null terminated the string. */
- *data_end = '\0';
-
- if (json_null != out_size) {
- *out_size = size;
- }
-
- return data;
-}
-
-json_weak int json_write_pretty_get_value_size(const struct json_value_s *value,
- size_t depth, size_t indent_size,
- size_t newline_size, size_t *size);
-
-json_weak int json_write_pretty_get_array_size(const struct json_array_s *array,
- size_t depth, size_t indent_size,
- size_t newline_size, size_t *size);
- int json_write_pretty_get_array_size(const struct json_array_s *array,
- size_t depth, size_t indent_size,
- size_t newline_size, size_t *size) {
- struct json_array_element_s *element;
-
- *size += 1; /* '['. */
-
- if (0 < array->length) {
- /* if we have any elements we need to add a newline after our '['. */
- *size += newline_size;
-
- *size += array->length - 1; /* ','s seperate each element. */
-
- for (element = array->start; json_null != element;
- element = element->next) {
- /* each element gets an indent. */
- *size += (depth + 1) * indent_size;
-
- if (json_write_pretty_get_value_size(element->value, depth + 1,
- indent_size, newline_size, size)) {
- /* value was malformed! */
- return 1;
- }
-
- /* each element gets a newline too. */
- *size += newline_size;
- }
-
- /* since we wrote out some elements, need to add a newline and indentation.
- */
- /* to the trailing ']'. */
- *size += depth * indent_size;
- }
-
- *size += 1; /* ']'. */
-
- return 0;
-}
-
-json_weak int json_write_pretty_get_object_size(const struct json_object_s *object,
- size_t depth, size_t indent_size,
- size_t newline_size,
- size_t *size);
- int json_write_pretty_get_object_size(const struct json_object_s *object,
- size_t depth, size_t indent_size,
- size_t newline_size,
- size_t *size) {
- struct json_object_element_s *element;
-
- *size += 1; /* '{'. */
-
- if (0 < object->length) {
- *size += newline_size; /* need a newline next. */
-
- *size += object->length - 1; /* ','s seperate each element. */
-
- for (element = object->start; json_null != element;
- element = element->next) {
- /* each element gets an indent and newline. */
- *size += (depth + 1) * indent_size;
- *size += newline_size;
-
- if (json_write_get_string_size(element->name, size)) {
- /* string was malformed! */
- return 1;
- }
-
- *size += 3; /* seperate each name/value pair with " : ". */
-
- if (json_write_pretty_get_value_size(element->value, depth + 1,
- indent_size, newline_size, size)) {
- /* value was malformed! */
- return 1;
- }
- }
-
- *size += depth * indent_size;
- }
-
- *size += 1; /* '}'. */
-
- return 0;
-}
-
-json_weak int json_write_pretty_get_value_size(const struct json_value_s *value,
- size_t depth, size_t indent_size,
- size_t newline_size, size_t *size);
- int json_write_pretty_get_value_size(const struct json_value_s *value,
- size_t depth, size_t indent_size,
- size_t newline_size, size_t *size) {
- switch (value->type) {
- default:
- /* unknown value type found! */
- return 1;
- case json_type_number:
- return json_write_get_number_size((struct json_number_s *)value->payload,
- size);
- case json_type_string:
- return json_write_get_string_size((struct json_string_s *)value->payload,
- size);
- case json_type_array:
- return json_write_pretty_get_array_size(
- (struct json_array_s *)value->payload, depth, indent_size, newline_size,
- size);
- case json_type_object:
- return json_write_pretty_get_object_size(
- (struct json_object_s *)value->payload, depth, indent_size,
- newline_size, size);
- case json_type_true:
- *size += 4; /* the string "true". */
- return 0;
- case json_type_false:
- *size += 5; /* the string "false". */
- return 0;
- case json_type_null:
- *size += 4; /* the string "null". */
- return 0;
- }
-}
-
-json_weak char *json_write_pretty_value(const struct json_value_s *value,
- size_t depth, const char *indent,
- const char *newline, char *data);
-
-json_weak char *json_write_pretty_array(const struct json_array_s *array,
- size_t depth, const char *indent,
- const char *newline, char *data);
- char *json_write_pretty_array(const struct json_array_s *array,
- size_t depth, const char *indent,
- const char *newline, char *data) {
- size_t k, m;
- struct json_array_element_s *element;
-
- *data++ = '['; /* open the array. */
-
- if (0 < array->length) {
- for (k = 0; '\0' != newline[k]; k++) {
- *data++ = newline[k];
- }
-
- for (element = array->start; json_null != element;
- element = element->next) {
- if (element != array->start) {
- *data++ = ','; /* ','s seperate each element. */
-
- for (k = 0; '\0' != newline[k]; k++) {
- *data++ = newline[k];
- }
- }
-
- for (k = 0; k < depth + 1; k++) {
- for (m = 0; '\0' != indent[m]; m++) {
- *data++ = indent[m];
- }
- }
-
- data = json_write_pretty_value(element->value, depth + 1, indent, newline,
- data);
-
- if (json_null == data) {
- /* value was malformed! */
- return json_null;
- }
- }
-
- for (k = 0; '\0' != newline[k]; k++) {
- *data++ = newline[k];
- }
-
- for (k = 0; k < depth; k++) {
- for (m = 0; '\0' != indent[m]; m++) {
- *data++ = indent[m];
- }
- }
- }
-
- *data++ = ']'; /* close the array. */
-
- return data;
-}
-
-json_weak char *json_write_pretty_object(const struct json_object_s *object,
- size_t depth, const char *indent,
- const char *newline, char *data);
- char *json_write_pretty_object(const struct json_object_s *object,
- size_t depth, const char *indent,
- const char *newline, char *data) {
- size_t k, m;
- struct json_object_element_s *element;
-
- *data++ = '{'; /* open the object. */
-
- if (0 < object->length) {
- for (k = 0; '\0' != newline[k]; k++) {
- *data++ = newline[k];
- }
-
- for (element = object->start; json_null != element;
- element = element->next) {
- if (element != object->start) {
- *data++ = ','; /* ','s seperate each element. */
-
- for (k = 0; '\0' != newline[k]; k++) {
- *data++ = newline[k];
- }
- }
-
- for (k = 0; k < depth + 1; k++) {
- for (m = 0; '\0' != indent[m]; m++) {
- *data++ = indent[m];
- }
- }
-
- data = json_write_string(element->name, data);
-
- if (json_null == data) {
- /* string was malformed! */
- return json_null;
- }
-
- /* " : "s seperate each name/value pair. */
- *data++ = ' ';
- *data++ = ':';
- *data++ = ' ';
-
- data = json_write_pretty_value(element->value, depth + 1, indent, newline,
- data);
-
- if (json_null == data) {
- /* value was malformed! */
- return json_null;
- }
- }
-
- for (k = 0; '\0' != newline[k]; k++) {
- *data++ = newline[k];
- }
-
- for (k = 0; k < depth; k++) {
- for (m = 0; '\0' != indent[m]; m++) {
- *data++ = indent[m];
- }
- }
- }
-
- *data++ = '}'; /* close the object. */
-
- return data;
-}
-
-json_weak char *json_write_pretty_value(const struct json_value_s *value,
- size_t depth, const char *indent,
- const char *newline, char *data);
- char *json_write_pretty_value(const struct json_value_s *value,
- size_t depth, const char *indent,
- const char *newline, char *data) {
- switch (value->type) {
- default:
- /* unknown value type found! */
- return json_null;
- case json_type_number:
- return json_write_number((struct json_number_s *)value->payload, data);
- case json_type_string:
- return json_write_string((struct json_string_s *)value->payload, data);
- case json_type_array:
- return json_write_pretty_array((struct json_array_s *)value->payload, depth,
- indent, newline, data);
- case json_type_object:
- return json_write_pretty_object((struct json_object_s *)value->payload,
- depth, indent, newline, data);
- case json_type_true:
- data[0] = 't';
- data[1] = 'r';
- data[2] = 'u';
- data[3] = 'e';
- return data + 4;
- case json_type_false:
- data[0] = 'f';
- data[1] = 'a';
- data[2] = 'l';
- data[3] = 's';
- data[4] = 'e';
- return data + 5;
- case json_type_null:
- data[0] = 'n';
- data[1] = 'u';
- data[2] = 'l';
- data[3] = 'l';
- return data + 4;
- }
-}
-
-void *json_write_pretty(const struct json_value_s *value, const char *indent,
- const char *newline, size_t *out_size) {
- size_t size = 0;
- size_t indent_size = 0;
- size_t newline_size = 0;
- char *data = json_null;
- char *data_end = json_null;
-
- if (json_null == value) {
- return json_null;
- }
-
- if (json_null == indent) {
- indent = " "; /* default to two spaces. */
- }
-
- if (json_null == newline) {
- newline = "\n"; /* default to linux newlines. */
- }
-
- while ('\0' != indent[indent_size]) {
- ++indent_size; /* skip non-null terminating characters. */
- }
-
- while ('\0' != newline[newline_size]) {
- ++newline_size; /* skip non-null terminating characters. */
- }
-
- if (json_write_pretty_get_value_size(value, 0, indent_size, newline_size,
- &size)) {
- /* value was malformed! */
- return json_null;
- }
-
- size += 1; /* for the '\0' null terminating character. */
-
- data = (char *)malloc(size);
-
- if (json_null == data) {
- /* malloc failed! */
- return json_null;
- }
-
- data_end = json_write_pretty_value(value, 0, indent, newline, data);
-
- if (json_null == data_end) {
- /* bad chi occurred! */
- free(data);
- return json_null;
- }
-
- /* null terminated the string. */
- *data_end = '\0';
-
- if (json_null != out_size) {
- *out_size = size;
- }
-
- return data;
-}
-
-#if defined(__clang__)
-#pragma clang diagnostic pop
-#elif defined(_MSC_VER)
-#pragma warning(pop)
-#endif
-
-#endif /* SHEREDOM_JSON_H_INCLUDED. */
diff --git a/src/main.c b/src/main.c
index 359347d..85a331b 100644
--- a/src/main.c
+++ b/src/main.c
@@ -7,7 +7,7 @@
#include "rss.h"
#include "rss_html_common.h"
#include "html.h"
-#include "json.h"
+#include "../depends/cJSON.h"
#include "alloc.h"
#include <stdio.h>
@@ -70,74 +70,65 @@ static void data_file_get_downloaded(const char *dir_name, const char *data_file
return;
}
- struct json_value_s *json_root = json_parse(file_data, file_size);
+ cJSON *json_root = cJSON_ParseWithLength(file_data, file_size);
if(!json_root) {
fprintf(stderr, "Failed to parse file %s as json\n", data_filepath);
goto cleanup;
}
+ free(file_data);
+ file_data = NULL;
- struct json_object_s *json_root_obj = json_value_as_object(json_root);
- if(!json_root_obj) {
+ if(!cJSON_IsObject(json_root)) {
fprintf(stderr, "File %s contains malformed json. Expected json root element to be an object\n", data_filepath);
goto cleanup;
}
- struct json_value_s *downloaded_json = json_object_get_field_by_name(json_root_obj, "downloaded");
+ const cJSON *downloaded_json = cJSON_GetObjectItemCaseSensitive(json_root, "downloaded");
if(!downloaded_json) {
fprintf(stderr, "File %s contains malformed json. Expected json to contain \"downloaded\"\n", data_filepath);
goto cleanup;
}
- struct json_array_s *downloaded_json_arr = json_value_as_array(downloaded_json);
- if(!downloaded_json_arr) {
+ if(!cJSON_IsArray(downloaded_json)) {
fprintf(stderr, "File %s contains malformed json. Expected \"downloaded\" to be an array\n", data_filepath);
goto cleanup;
}
size_t dir_name_len = strlen(dir_name);
- struct json_array_element_s *downloaded_item = downloaded_json_arr->start;
- for(; downloaded_item; downloaded_item = downloaded_item->next) {
- struct json_object_s *downloaded_item_obj = json_value_as_object(downloaded_item->value);
- if(!downloaded_item_obj)
+ const cJSON *downloaded_item_json = NULL;
+ cJSON_ArrayForEach(downloaded_item_json, downloaded_json) {
+ if(!cJSON_IsObject(downloaded_item_json))
continue;
- struct json_value_s *time_json = json_object_get_field_by_name(downloaded_item_obj, "time");
- if(!time_json || time_json->type != json_type_string)
+ const cJSON *time_json = cJSON_GetObjectItemCaseSensitive(downloaded_item_json, "time");
+ if(!time_json || !cJSON_IsString(time_json))
continue;
- struct json_string_s *time_json_str = json_value_as_string(time_json);
- struct json_value_s *title_json = json_object_get_field_by_name(downloaded_item_obj, "title");
- struct json_string_s *title_str = NULL;
- if(title_json && title_json->type == json_type_string)
- title_str = json_value_as_string(title_json);
-
- struct json_value_s *filename_json = json_object_get_field_by_name(downloaded_item_obj, "filename");
- struct json_string_s *filename_str = NULL;
- if(filename_json && filename_json->type == json_type_string)
- filename_str = json_value_as_string(filename_json);
+ const cJSON *title_json = cJSON_GetObjectItemCaseSensitive(downloaded_item_json, "title");
+ const cJSON *filename_json = cJSON_GetObjectItemCaseSensitive(downloaded_item_json, "filename");
/* Filename limit is 256, so this should be safe... */
char title[256];
- if(filename_str) {
- strcpy(title, filename_str->string);
- } else if(title_str) {
+ if(filename_json && cJSON_IsString(filename_json)) {
+ strcpy(title, filename_json->valuestring);
+ } else if(title_json && cJSON_IsString(title_json)) {
if(is_html) {
strcpy(title, dir_name);
title[dir_name_len] = '/';
- strcpy(title + dir_name_len + 1, title_str->string);
+ strcpy(title + dir_name_len + 1, title_json->valuestring);
} else {
- strcpy(title, title_str->string);
+ strcpy(title, title_json->valuestring);
}
} else {
continue;
}
- callback(title, atof(time_json_str->string), userdata);
+ callback(title, atof(time_json->valuestring), userdata);
}
cleanup:
- free(json_root);
+ cJSON_Delete(json_root);
free(file_data);
}
@@ -273,7 +264,7 @@ int is_program_running() {
}
/* plugin is NULL for rss */
-typedef int (*IterateTrackedItemCallback)(char *title, char *link, char *plugin, char *config_dir, struct json_object_s *json_data, void *userdata);
+typedef int (*IterateTrackedItemCallback)(char *title, char *link, char *plugin, char *config_dir, cJSON *json_data, void *userdata);
static void iterate_tracked_items(char *config_dir, IterateTrackedItemCallback iterate_callback, void *userdata) {
/*
TODO: Only iterate updated items. To make that work, sync_rss and sync_html need to be updated to update
@@ -312,7 +303,7 @@ static void iterate_tracked_items(char *config_dir, IterateTrackedItemCallback i
char *link_file_content = NULL;
char *data_file_content = NULL;
char *plugin_file_content = NULL;
- struct json_value_s *json_data = NULL;
+ cJSON *json_data = NULL;
strcpy(item_filepath + item_filepath_len + title_len, "/link");
long link_file_size = 0;
@@ -331,19 +322,19 @@ static void iterate_tracked_items(char *config_dir, IterateTrackedItemCallback i
goto cleanup_item;
}
- json_data = json_parse(data_file_content, data_file_size);
- if(!json_data || json_data->type != json_type_object) {
+ json_data = cJSON_ParseWithLength(data_file_content, data_file_size);
+ if(!json_data || !cJSON_IsObject(json_data)) {
fprintf(stderr, "Rss corrupt for %s\n", dir->d_name);
goto cleanup_item;
}
free(data_file_content);
data_file_content = NULL;
- if(iterate_callback(dir->d_name, link_file_content, plugin_file_content, config_dir, json_value_as_object(json_data), userdata) != 0)
+ if(iterate_callback(dir->d_name, link_file_content, plugin_file_content, config_dir, json_data, userdata) != 0)
fprintf(stderr, "Failed to sync %s\n", dir->d_name);
cleanup_item:
- free(json_data);
+ cJSON_Delete(json_data);
free(plugin_file_content);
free(data_file_content);
free(link_file_content);
@@ -352,7 +343,7 @@ static void iterate_tracked_items(char *config_dir, IterateTrackedItemCallback i
closedir(d);
}
-static int iterate_tracked_item_rss_callback(char *title, char *link, char *plugin, char *config_dir, struct json_object_s *json_data, void *userdata) {
+static int iterate_tracked_item_rss_callback(char *title, char *link, char *plugin, char *config_dir, cJSON *json_data, void *userdata) {
(void)plugin;
TransmissionSession *transmission_session = userdata;
TrackedRss tracked_rss;
@@ -367,7 +358,7 @@ typedef struct {
const char *download_dir;
} IterateHtmlItemUserdata;
-static int iterate_tracked_item_html_callback(char *title, char *link, char *plugin, char *config_dir, struct json_object_s *json_data, void *userdata) {
+static int iterate_tracked_item_html_callback(char *title, char *link, char *plugin, char *config_dir, cJSON *json_data, void *userdata) {
if(!plugin) {
fprintf(stderr, "Missing plugin name for html item: %s\n", title);
return -1;
diff --git a/src/rss.c b/src/rss.c
index 8d56236..29a7708 100644
--- a/src/rss.c
+++ b/src/rss.c
@@ -6,7 +6,7 @@
#include "buffer.h"
#include "episode.h"
#include "rss_html_common.h"
-#include "json.h"
+#include "../depends/cJSON.h"
#include "alloc.h"
#include <string.h>
#include <stdio.h>
@@ -435,31 +435,23 @@ int add_rss(const char *name, char *url, char *rss_config_dir, const char *start
static int is_item_already_downloaded(const char *title, const char *link, TrackedRss *tracked_rss) {
/* TODO: Optimize this... */
- struct json_value_s *downloaded_json = json_object_get_field_by_name(tracked_rss->json_data, "downloaded");
- if(downloaded_json && downloaded_json->type == json_type_array) {
- struct json_array_s *downloaded_json_array = json_value_as_array(downloaded_json);
- struct json_array_element_s *downloaded_item = downloaded_json_array->start;
- for(; downloaded_item; downloaded_item = downloaded_item->next) {
- struct json_object_s *downloaded_obj = json_value_as_object(downloaded_item->value);
- if(!downloaded_obj)
- continue;
-
- struct json_value_s *download_title_value = json_object_get_field_by_name(downloaded_obj, "title");
- struct json_value_s *download_url_value = json_object_get_field_by_name(downloaded_obj, "url");
-
- struct json_string_s *download_title_str = NULL;
- struct json_string_s *download_url_str = NULL;
+ cJSON *downloaded_json = cJSON_GetObjectItemCaseSensitive(tracked_rss->json_data, "downloaded");
+ if(!cJSON_IsArray(downloaded_json))
+ return 0;
- if(download_title_value)
- download_title_str = json_value_as_string(download_title_value);
+ cJSON *downloaded_item = NULL;
+ cJSON_ArrayForEach(downloaded_item, downloaded_json) {
+ if(!cJSON_IsObject(downloaded_item))
+ continue;
- if(download_url_value)
- download_url_str = json_value_as_string(download_url_value);
+ cJSON *download_title_value = cJSON_GetObjectItemCaseSensitive(downloaded_item, "title");
+ cJSON *download_url_value = cJSON_GetObjectItemCaseSensitive(downloaded_item, "url");
- if((download_title_str && strcmp(download_title_str->string, title) == 0) || (download_url_str && strcmp(download_url_str->string, link) == 0))
- return 1;
- }
+ if((cJSON_IsString(download_title_value) && strcmp(download_title_value->valuestring, title) == 0)
+ || (cJSON_IsString(download_url_value) && strcmp(download_url_value->valuestring, link) == 0))
+ return 1;
}
+
return 0;
}
diff --git a/src/rss.h b/src/rss.h
index c2b6a49..87d064f 100644
--- a/src/rss.h
+++ b/src/rss.h
@@ -1,13 +1,13 @@
#ifndef RSS_H
#define RSS_H
-struct json_object_s;
+typedef struct cJSON cJSON;
struct TransmissionSession;
typedef struct {
char *title;
char *link;
- struct json_object_s *json_data;
+ cJSON *json_data;
} TrackedRss;
int add_rss(const char *name, char *url, char *rss_config_dir, const char *start_after);
diff --git a/src/rss_html_common.c b/src/rss_html_common.c
index 168195e..e8e98a5 100644
--- a/src/rss_html_common.c
+++ b/src/rss_html_common.c
@@ -1,164 +1,58 @@
#include "rss_html_common.h"
-#include "json.h"
+#include "../depends/cJSON.h"
#include "fileutils.h"
#include <string.h>
#include <stdio.h>
+#include <stdlib.h>
#include <time.h>
#include <assert.h>
int write_plugin_json_to_file(const char *dir, const char *filename, const char *url, const char *updated, const char *start_after, const char *start_after_url, const char *plugin_name) {
- struct json_string_s title_json_key;
- create_json_string(&title_json_key, "title", 5);
-
- struct json_string_s title_json_value_str;
- create_json_string(&title_json_value_str, start_after, start_after ? strlen(start_after) : 0);
- struct json_value_s title_json_value;
- init_json_value_str(&title_json_value, &title_json_value_str);
-
- struct json_string_s time_json_key;
- create_json_string(&time_json_key, "time", 4);
-
- struct json_string_s time_value_str;
- create_json_string(&time_value_str, updated, strlen(updated));
- struct json_value_s time_json_value;
- init_json_value_str(&time_json_value, &time_value_str);
-
- struct json_string_s url_json_key;
- create_json_string(&url_json_key, "url", 3);
-
- struct json_string_s url_value_str;
- create_json_string(&url_value_str, start_after_url, start_after_url ? strlen(start_after_url) : 0);
- struct json_value_s url_json_value;
- init_json_value_str(&url_json_value, &url_value_str);
-
- struct json_string_s plugin_json_key;
- create_json_string(&plugin_json_key, "plugin", 6);
-
- struct json_string_s plugin_json_value_str;
- create_json_string(&plugin_json_value_str, plugin_name, plugin_name ? strlen(plugin_name) : 0);
- struct json_value_s plugin_json_value;
- init_json_value_str(&plugin_json_value, &plugin_json_value_str);
-
- struct json_string_s link_json_key;
- create_json_string(&link_json_key, "link", 4);
-
- struct json_string_s link_json_value_str;
- create_json_string(&link_json_value_str, url, strlen(url));
- struct json_value_s link_json_value;
- init_json_value_str(&link_json_value, &link_json_value_str);
-
- struct json_string_s updated_json_key;
- create_json_string(&updated_json_key, "updated", 7);
-
- struct json_string_s updated_json_value_str;
- create_json_string(&updated_json_value_str, updated, strlen(updated));
- struct json_value_s updated_json_value;
- init_json_value_str(&updated_json_value, &updated_json_value_str);
-
- struct json_string_s downloaded_json_key;
- create_json_string(&downloaded_json_key, "downloaded", 10);
-
- struct json_object_s downloaded_json;
- downloaded_json.start = NULL;
- downloaded_json.length = 0;
-
- struct json_value_s downloaded_json_value;
- downloaded_json_value.payload = &downloaded_json;
- downloaded_json_value.type = json_type_object;
-
- struct json_object_element_s downloaded_title_element;
- downloaded_title_element.name = &title_json_key;
- downloaded_title_element.value = &title_json_value;
-
- struct json_object_element_s downloaded_time_element;
- downloaded_time_element.name = &time_json_key;
- downloaded_time_element.value = &time_json_value;
-
- struct json_object_element_s downloaded_url_element;
- downloaded_url_element.name = &url_json_key;
- downloaded_url_element.value = &url_json_value;
-
- downloaded_title_element.next = &downloaded_time_element;
- downloaded_time_element.next = &downloaded_url_element;
- downloaded_url_element.next = NULL;
-
- struct json_array_s downloaded_json_array;
- downloaded_json_array.start = NULL;
- downloaded_json_array.length = 0;
- struct json_array_element_s downloaded_json_array_element;
-
- if(start_after) {
- downloaded_json.start = &downloaded_title_element;
- downloaded_json.length = 3;
+ int result = 0;
- downloaded_json_array_element.value = &downloaded_json_value;
- downloaded_json_array_element.next = NULL;
- downloaded_json_array.start = &downloaded_json_array_element;
- downloaded_json_array.length = 1;
+ cJSON *json_body = cJSON_CreateObject();
+ if(!json_body) {
+ result = -1;
+ goto cleanup;
}
- struct json_value_s downloaded_json_array_value;
- downloaded_json_array_value.payload = &downloaded_json_array;
- downloaded_json_array_value.type = json_type_array;
+ cJSON_AddStringToObject(json_body, "link", url);
+ cJSON_AddStringToObject(json_body, "updated", updated);
+ if(plugin_name)
+ cJSON_AddStringToObject(json_body, "plugin", plugin_name);
- struct json_object_s json_root;
- json_root.length = 3;
-
- struct json_object_element_s link_element;
- link_element.name = &link_json_key;
- link_element.value = &link_json_value;
-
- struct json_object_element_s updated_element;
- updated_element.name = &updated_json_key;
- updated_element.value = &updated_json_value;
-
- struct json_object_element_s downloaded_element;
- downloaded_element.name = &downloaded_json_key;
- downloaded_element.value = &downloaded_json_array_value;
-
- struct json_object_element_s plugin_element;
- plugin_element.name = &plugin_json_key;
- plugin_element.value = &plugin_json_value;
+ cJSON *downloaded_item_json = cJSON_CreateObject();
+ if(!downloaded_item_json) {
+ result = -1;
+ goto cleanup;
+ }
- link_element.next = &updated_element;
- updated_element.next = &downloaded_element;
- downloaded_element.next = NULL;
+ if(start_after)
+ cJSON_AddStringToObject(downloaded_item_json, "title", start_after);
+ if(start_after_url)
+ cJSON_AddStringToObject(downloaded_item_json, "url", start_after_url);
+ cJSON_AddStringToObject(downloaded_item_json, "time", updated);
- if(plugin_name) {
- json_root.start = &plugin_element;
- plugin_element.next = &link_element;
- } else {
- json_root.start = &link_element;
+ cJSON *downloaded_json = cJSON_AddArrayToObject(json_body, "downloaded");
+ if(!downloaded_json) {
+ result = -1;
+ goto cleanup;
}
+ cJSON_AddItemToArray(downloaded_json, downloaded_item_json);
- struct json_value_s json_root_value;
- json_root_value.payload = &json_root;
- json_root_value.type = json_type_object;
- size_t json_body_size = 0;
- char *json_body_str = json_write_pretty(&json_root_value, " ", "\n", &json_body_size);
+ char *json_body_str = cJSON_Print(json_body);
if(!json_body_str) {
- fprintf(stderr, "Failed to write json data to file %s/%s\n", dir, filename);
- return -1;
+ result = -1;
+ goto cleanup;
}
-
- /* Workaround json bug (?) */
- json_body_size = strlen(json_body_str);
-
- int result = file_overwrite_in_dir(dir, filename, json_body_str, json_body_size);
+ result = file_overwrite_in_dir(dir, filename, json_body_str, strlen(json_body_str));
free(json_body_str);
- return result;
-}
-static struct json_array_element_s* get_last_element_in_json_array(struct json_array_s *json_array) {
- struct json_array_element_s *json_element = json_array->start;
- while(json_element) {
- struct json_array_element_s *next_json_element = json_element->next;
- if(next_json_element)
- json_element = next_json_element;
- else
- return json_element;
- }
- return NULL;
+ cleanup:
+ cJSON_Delete(json_body);
+ if(result != 0)
+ fprintf(stderr, "Failed to write json data to file %s/%s\n", dir, filename);
+ return result;
}
/* TODO: If this fails in the middle, recover and update the next time somehow */
@@ -184,193 +78,51 @@ int tracked_item_update_latest(TrackedItem *tracked_item, char *tracked_dir, Dow
goto cleanup;
}
- struct json_value_s *updated_field = json_object_get_field_by_name(tracked_item->json_data, "updated");
- if(updated_field) {
- struct json_string_s *updated_json = json_value_as_string(updated_field);
- updated_json->string = updated;
- updated_json->string_size = updated_len;
+ cJSON *updated_field = cJSON_GetObjectItemCaseSensitive(tracked_item->json_data, "updated");
+ if(updated_field && cJSON_IsString(updated_field)) {
+ cJSON_SetValuestring(updated_field, updated);
} else {
fprintf(stderr, "Corrupt json for rss item: %s\n", item_filepath);
}
- struct json_value_s *downloaded_json = json_object_get_field_by_name(tracked_item->json_data, "downloaded");
- struct json_array_s *downloaded_json_array_orig = NULL;
- struct json_array_s *downloaded_json_array = NULL;
-
- struct json_array_s new_downloaded_array;
- new_downloaded_array.length = 1;
- new_downloaded_array.start = NULL;
-
- struct json_value_s new_downloaded_array_val;
- new_downloaded_array_val.payload = &new_downloaded_array;
- new_downloaded_array_val.type = json_type_array;
-
- struct json_string_s downloaded_json_key;
- create_json_string(&downloaded_json_key, "downloaded", 10);
-
- struct json_object_element_s new_downloaded_array_obj_el;
- new_downloaded_array_obj_el.name = &downloaded_json_key;
- new_downloaded_array_obj_el.value = &new_downloaded_array_val;
-
- if(downloaded_json) {
- downloaded_json_array = json_value_as_array(downloaded_json);
- if(!downloaded_json_array) {
- fprintf(stderr, "Corrupt json for rss item: %s\n", item_filepath);
- result = -1;
- goto cleanup;
- }
- downloaded_json_array_orig = downloaded_json_array;
- } else {
- downloaded_json_array = &new_downloaded_array;
+ cJSON *downloaded_json = cJSON_GetObjectItemCaseSensitive(tracked_item->json_data, "downloaded");
+ if(!downloaded_json)
+ downloaded_json = cJSON_AddArrayToObject(tracked_item->json_data, "downloaded");
+ if(!downloaded_json || !cJSON_IsArray(downloaded_json)) {
+ result = -1;
+ goto cleanup;
}
- struct json_string_s title_json_key;
- create_json_string(&title_json_key, "title", 5);
-
- struct json_string_s filename_json_key;
- create_json_string(&filename_json_key, "filename", 8);
-
- struct json_string_s time_json_key;
- create_json_string(&time_json_key, "time", 4);
-
- struct json_string_s url_json_key;
- create_json_string(&url_json_key, "url", 3);
-
- struct json_string_s title_json_value_str[MAX_UPDATE_ITEMS];
- struct json_value_s title_json_value[MAX_UPDATE_ITEMS];
- struct json_string_s filename_json_value_str[MAX_UPDATE_ITEMS];
- struct json_value_s filename_json_value[MAX_UPDATE_ITEMS];
- struct json_string_s time_value_str[MAX_UPDATE_ITEMS];
- struct json_value_s time_json_value[MAX_UPDATE_ITEMS];
- struct json_string_s url_value_str[MAX_UPDATE_ITEMS];
- struct json_value_s url_json_value[MAX_UPDATE_ITEMS];
-
- struct json_object_element_s downloaded_title_element[MAX_UPDATE_ITEMS];
- struct json_object_element_s downloaded_filename_element[MAX_UPDATE_ITEMS];
- struct json_object_element_s downloaded_time_element[MAX_UPDATE_ITEMS];
- struct json_object_element_s downloaded_url_element[MAX_UPDATE_ITEMS];
-
- struct json_object_s new_downloaded_json_obj[MAX_UPDATE_ITEMS];
- struct json_value_s new_downloaded_json_val[MAX_UPDATE_ITEMS];
- struct json_array_element_s new_downloaded_item_element[MAX_UPDATE_ITEMS];
-
- char items_timestamps[MAX_UPDATE_ITEMS][32];
-
- struct json_array_element_s *last_downloaded_element = NULL;
for(int i = 0; i < num_download_items; ++i) {
- create_json_string(&title_json_value_str[i], download_items[i]->title, strlen(download_items[i]->title));
- init_json_value_str(&title_json_value[i], &title_json_value_str[i]);
-
- if(filenames) {
- create_json_string(&filename_json_value_str[i], filenames[i], strlen(filenames[i]));
- init_json_value_str(&filename_json_value[i], &filename_json_value_str[i]);
- }
-
- sprintf(items_timestamps[i], "%ld", timestamps[i]);
- int updated_len = strlen(items_timestamps[i]);
- create_json_string(&time_value_str[i], items_timestamps[i], updated_len);
- init_json_value_str(&time_json_value[i], &time_value_str[i]);
-
- create_json_string(&url_value_str[i], download_items[i]->link, strlen(download_items[i]->link));
- init_json_value_str(&url_json_value[i], &url_value_str[i]);
-
- downloaded_title_element[i].name = &title_json_key;
- downloaded_title_element[i].value = &title_json_value[i];
-
- if(filenames) {
- downloaded_filename_element[i].name = &filename_json_key;
- downloaded_filename_element[i].value = &filename_json_value[i];
- }
-
- downloaded_time_element[i].name = &time_json_key;
- downloaded_time_element[i].value = &time_json_value[i];
-
- downloaded_url_element[i].name = &url_json_key;
- downloaded_url_element[i].value = &url_json_value[i];
-
- downloaded_title_element[i].next = &downloaded_time_element[i];
- downloaded_time_element[i].next = &downloaded_url_element[i];
- downloaded_url_element[i].next = NULL;
- if(filenames) {
- downloaded_url_element[i].next = &downloaded_filename_element[i];
- downloaded_filename_element[i].next = NULL;
- new_downloaded_json_obj[i].length = 4;
- } else {
- new_downloaded_json_obj[i].length = 3;
+ cJSON *downloaded_item_json = cJSON_CreateObject();
+ if(!downloaded_item_json) {
+ result = -1;
+ goto cleanup;
}
- new_downloaded_json_obj[i].start = &downloaded_title_element[i];
-
- new_downloaded_json_val[i].payload = &new_downloaded_json_obj[i];
- new_downloaded_json_val[i].type = json_type_object;
-
- new_downloaded_item_element[i].value = &new_downloaded_json_val[i];
- new_downloaded_item_element[i].next = NULL;
+ char timestamp[32];
+ snprintf(timestamp, sizeof(timestamp), "%ld", timestamps[i]);
- if(downloaded_json_array->length > 0) {
- if(!last_downloaded_element)
- last_downloaded_element = get_last_element_in_json_array(downloaded_json_array);
-
- if(last_downloaded_element)
- last_downloaded_element->next = &new_downloaded_item_element[i];
- else
- downloaded_json_array->start = &new_downloaded_item_element[i];
-
- last_downloaded_element = &new_downloaded_item_element[i];
- downloaded_json_array->length++;
- } else {
- downloaded_json_array->start = &new_downloaded_item_element[i];
- downloaded_json_array->length = 1;
- last_downloaded_element = &new_downloaded_item_element[i];
+ cJSON_AddStringToObject(downloaded_item_json, "title", download_items[i]->title);
+ cJSON_AddStringToObject(downloaded_item_json, "time", timestamp);
+ cJSON_AddStringToObject(downloaded_item_json, "url", download_items[i]->link);
+ if(filenames)
+ cJSON_AddStringToObject(downloaded_item_json, "filename", filenames[i]);
- if(!downloaded_json_array_orig) {
- struct json_object_element_s *prev_start = tracked_item->json_data->start;
- tracked_item->json_data->start = &new_downloaded_array_obj_el;
- new_downloaded_array_obj_el.next = prev_start;
- tracked_item->json_data->length++;
- }
- }
+ cJSON_AddItemToArray(downloaded_json, downloaded_item_json);
}
- struct json_value_s json_root_value;
- json_root_value.payload = tracked_item->json_data;
- json_root_value.type = json_type_object;
-
- size_t json_body_size = 0;
- char *json_body_str = json_write_pretty(&json_root_value, " ", "\n", &json_body_size);
+ char *json_body_str = cJSON_Print(tracked_item->json_data);
if(!json_body_str) {
- fprintf(stderr, "Failed to write json data to file %s/data\n", item_filepath);
result = -1;
goto cleanup;
}
-
- /* Workaround json bug (?) */
- json_body_size = strlen(json_body_str);
-
- result = file_overwrite_in_dir(item_filepath, "data", json_body_str, json_body_size);
+ result = file_overwrite_in_dir(item_filepath, "data", json_body_str, strlen(json_body_str));
free(json_body_str);
cleanup:
+ if(result != 0)
+ fprintf(stderr, "Failed to update data for item: %s\n", item_filepath);
tracked_dir[tracked_dir_len] = '\0';
return result;
}
-
-struct json_value_s* json_object_get_field_by_name(struct json_object_s *json_obj, const char *name) {
- struct json_object_element_s *obj_element = json_obj->start;
- while(obj_element) {
- if(strcmp(obj_element->name->string, name) == 0)
- return obj_element->value;
- obj_element = obj_element->next;
- }
- return NULL;
-}
-
-void create_json_string(struct json_string_s *json_result, const char *str, int len) {
- json_result->string = str;
- json_result->string_size = len;
-}
-
-void init_json_value_str(struct json_value_s *json_value, struct json_string_s *json_str) {
- json_value->payload = json_str;
- json_value->type = json_type_string;
-}
diff --git a/src/rss_html_common.h b/src/rss_html_common.h
index 2f9e053..085ddc1 100644
--- a/src/rss_html_common.h
+++ b/src/rss_html_common.h
@@ -3,9 +3,7 @@
#define MAX_UPDATE_ITEMS 10
-struct json_value_s;
-struct json_object_s;
-struct json_string_s;
+typedef struct cJSON cJSON;
typedef struct {
const char *title;
@@ -15,7 +13,7 @@ typedef struct {
typedef struct {
char *title;
char *link;
- struct json_object_s *json_data;
+ cJSON *json_data;
} TrackedItem;
int write_plugin_json_to_file(const char *dir, const char *filename, const char *url, const char *updated, const char *start_after, const char *start_after_url, const char *plugin_name);
@@ -29,8 +27,5 @@ int write_plugin_json_to_file(const char *dir, const char *filename, const char
@tracked_dir is also modified and then restored at the end.
*/
int tracked_item_update_latest(TrackedItem *tracked_item, char *tracked_dir, DownloadItemsData **download_items, char **filenames, long *timestamps, int num_download_items);
-struct json_value_s* json_object_get_field_by_name(struct json_object_s *json_obj, const char *name);
-void create_json_string(struct json_string_s *json_result, const char *str, int len);
-void init_json_value_str(struct json_value_s *json_value, struct json_string_s *json_str);
#endif \ No newline at end of file
diff --git a/src/transmission.c b/src/transmission.c
index d08c74c..916ccc7 100644
--- a/src/transmission.c
+++ b/src/transmission.c
@@ -1,7 +1,7 @@
#include "transmission.h"
#include "program.h"
#include "buffer.h"
-#include "json.h"
+#include "../depends/cJSON.h"
#include "rss_html_common.h"
#include <stdio.h>
#include <string.h>
@@ -74,61 +74,40 @@ int transmission_start_daemon(const char *download_dir) {
return 0;
}
-static struct json_object_s* json_object_get_child_object_by_name(struct json_object_s *json_obj, const char *field_name) {
- struct json_value_s *json_child_obj = json_object_get_field_by_name(json_obj, field_name);
- if(!json_child_obj || json_child_obj->type != json_type_object)
- return NULL;
- return (struct json_object_s*)json_child_obj->payload;
-}
-
-static struct json_string_s* json_object_get_child_string_by_name(struct json_object_s *json_obj, const char *field_name) {
- struct json_value_s *json_child_str = json_object_get_field_by_name(json_obj, field_name);
- if(!json_child_str || json_child_str->type != json_type_string)
- return NULL;
- return (struct json_string_s*)json_child_str->payload;
-}
-
-static struct json_number_s* json_object_get_child_number_by_name(struct json_object_s *json_obj, const char *field_name) {
- struct json_value_s *json_number_str = json_object_get_field_by_name(json_obj, field_name);
- if(!json_number_str || json_number_str->type != json_type_number)
- return NULL;
- return (struct json_number_s*)json_number_str->payload;
-}
-
-static int transmission_response_is_success(struct json_object_s *json_root) {
- struct json_string_s *result_field = json_object_get_child_string_by_name(json_root, "result");
- if(!result_field) {
+static int transmission_response_is_success(cJSON *json_root) {
+ cJSON *result_field = cJSON_GetObjectItemCaseSensitive(json_root, "result");
+ if(!result_field || !cJSON_IsString(result_field)) {
fprintf(stderr, "Error: Transmission response is missing result field\n");
return -1;
}
- return strcmp(result_field->string, "success");
+ return strcmp(result_field->valuestring, "success");
}
-static int transmission_add_torrent_response_get_torrent_name(struct json_object_s *json_root, int *torrent_id, const char **torrent_name) {
- struct json_object_s *arguments_obj = json_object_get_child_object_by_name(json_root, "arguments");
- if(!arguments_obj) {
+static int transmission_add_torrent_response_get_torrent_name(cJSON *json_root, int *torrent_id, const char **torrent_name) {
+ cJSON *arguments_obj = cJSON_GetObjectItemCaseSensitive(json_root, "arguments");
+ if(!cJSON_IsObject(arguments_obj)) {
fprintf(stderr, "Error: transmission add torrent response is missing arguments field or its not an object\n");
return -1;
}
- struct json_object_s *torrent_added_obj = json_object_get_child_object_by_name(arguments_obj, "torrent-added");
- if(!torrent_added_obj)
- torrent_added_obj = json_object_get_child_object_by_name(arguments_obj, "torrent-duplicate");
+ cJSON *torrent_added_obj = cJSON_GetObjectItemCaseSensitive(arguments_obj, "torrent-added");
+ if(!cJSON_IsObject(torrent_added_obj))
+ torrent_added_obj = cJSON_GetObjectItemCaseSensitive(arguments_obj, "torrent-duplicate");
- if(!torrent_added_obj) {
+ if(!cJSON_IsObject(torrent_added_obj)) {
fprintf(stderr, "Error: transmission add torrent response is missing both torrent-added and torrent-duplicate argument\n");
return -1;
}
if(torrent_id) {
- struct json_number_s *torrent_id_field = json_object_get_child_number_by_name(torrent_added_obj, "id");
- if(!torrent_id_field) {
+ cJSON *torrent_id_field = cJSON_GetObjectItemCaseSensitive(torrent_added_obj, "id");
+ if(!cJSON_IsNumber(torrent_id_field)) {
fprintf(stderr, "Error: transmission add torrent response is missing torrent id\n");
return -1;
}
- *torrent_id = atoi(torrent_id_field->number);
+ *torrent_id = torrent_id_field->valuedouble;
if(*torrent_id == 0) {
fprintf(stderr, "Error: transmission add torrent response has invalid torrent id\n");
return -1;
@@ -136,12 +115,12 @@ static int transmission_add_torrent_response_get_torrent_name(struct json_object
}
if(torrent_name) {
- struct json_string_s *torrent_name_field = json_object_get_child_string_by_name(torrent_added_obj, "name");
- if(!torrent_name_field) {
+ cJSON *torrent_name_field = cJSON_GetObjectItemCaseSensitive(torrent_added_obj, "name");
+ if(!cJSON_IsString(torrent_name_field)) {
fprintf(stderr, "Error: transmission add torrent response is missing torrent name\n");
return -1;
}
- *torrent_name = torrent_name_field->string;
+ *torrent_name = torrent_name_field->valuestring;
}
return 0;
@@ -151,7 +130,7 @@ int transmission_add_torrent(TransmissionSession *session, const char *url, int
int result = 0;
Buffer buffer;
buffer_init(&buffer);
- struct json_value_s *json_response_val = NULL;
+ cJSON *json_response = NULL;
/* TODO: json escape url */
char request[4096];
@@ -180,16 +159,14 @@ int transmission_add_torrent(TransmissionSession *session, const char *url, int
}
}
- json_response_val = json_parse(buffer.data, buffer.size);
- if(!json_response_val || json_response_val->type != json_type_object) {
+ json_response = cJSON_ParseWithLength(buffer.data, buffer.size);
+ if(!json_response || !cJSON_IsObject(json_response)) {
fprintf(stderr, "Failed to parse torrent add response: %.*s as json\n", (int)buffer.size, (char*)buffer.data);
result = -1;
goto cleanup;
}
-
- struct json_object_s *json_response_obj = json_value_as_object(json_response_val);
- if(transmission_response_is_success(json_response_obj) != 0) {
+ if(transmission_response_is_success(json_response) != 0) {
fprintf(stderr, "Error: transmission torrent add request failed, response: %.*s\n", (int)buffer.size, (char*)buffer.data);
result = -1;
goto cleanup;
@@ -197,7 +174,7 @@ int transmission_add_torrent(TransmissionSession *session, const char *url, int
int response_torrent_id;
const char *response_torrent_name;
- result = transmission_add_torrent_response_get_torrent_name(json_response_obj,
+ result = transmission_add_torrent_response_get_torrent_name(json_response,
torrent_id ? &response_torrent_id : NULL,
torrent_name ? &response_torrent_name : NULL);
@@ -211,7 +188,7 @@ int transmission_add_torrent(TransmissionSession *session, const char *url, int
*torrent_name = strdup(response_torrent_name);
cleanup:
- free(json_response_val);
+ cJSON_Delete(json_response);
buffer_deinit(&buffer);
return result;
}
@@ -220,7 +197,7 @@ int transmission_list_torrents(TransmissionSession *session, TorrentListCallback
int result = 0;
Buffer buffer;
buffer_init(&buffer);
- struct json_value_s *json_response_val = NULL;
+ cJSON *json_response = NULL;
char request[100];
int written_bytes = snprintf(request, sizeof(request), "{ \"arguments\": { \"fields\": [ \"id\", \"name\", \"percentDone\" ] }, \"method\": \"torrent-get\" }");
@@ -248,62 +225,49 @@ int transmission_list_torrents(TransmissionSession *session, TorrentListCallback
}
}
- json_response_val = json_parse(buffer.data, buffer.size);
- if(!json_response_val || json_response_val->type != json_type_object) {
+ json_response = cJSON_ParseWithLength(buffer.data, buffer.size);
+ if(!json_response || !cJSON_IsObject(json_response)) {
fprintf(stderr, "Failed to parse torrent list response: %.*s as json\n", (int)buffer.size, (char*)buffer.data);
result = -1;
goto cleanup;
}
-
- struct json_object_s *json_response_obj = json_value_as_object(json_response_val);
- if(transmission_response_is_success(json_response_obj) != 0) {
+ if(transmission_response_is_success(json_response) != 0) {
fprintf(stderr, "Error: transmission torrent list request failed, response: %.*s\n", (int)buffer.size, (char*)buffer.data);
result = -1;
goto cleanup;
}
- struct json_value_s *arguments_field = json_object_get_field_by_name(json_response_obj, "arguments");
- if(!arguments_field || arguments_field->type != json_type_object) {
+ cJSON *arguments_field = cJSON_GetObjectItemCaseSensitive(json_response, "arguments");
+ if(!cJSON_IsObject(arguments_field)) {
fprintf(stderr, "Error: transmission torrent list response is missing arguments or its not an object\n");
result = -1;
goto cleanup;
}
- struct json_object_s *arguments_field_obj = json_value_as_object(arguments_field);
- struct json_value_s *torrents_field = json_object_get_field_by_name(arguments_field_obj, "torrents");
- if(!torrents_field || torrents_field->type != json_type_array) {
+ cJSON *torrents_field = cJSON_GetObjectItemCaseSensitive(arguments_field, "torrents");
+ if(!cJSON_IsArray(torrents_field)) {
fprintf(stderr, "Error: transmission torrent list response is missing torrents or its not an array\n");
result = -1;
goto cleanup;
}
- struct json_array_element_s *torrent_item = json_value_as_array(torrents_field)->start;
- while(torrent_item) {
- struct json_object_s *torrent_item_obj = json_value_as_object(torrent_item->value);
- if(!torrent_item_obj)
+ cJSON *torrent_item = NULL;
+ cJSON_ArrayForEach(torrent_item, torrents_field) {
+ if(!cJSON_IsObject(torrent_item))
continue;
- struct json_value_s *id_field = json_object_get_field_by_name(torrent_item_obj, "id");
- struct json_value_s *name_field = json_object_get_field_by_name(torrent_item_obj, "name");
- struct json_value_s *percent_done_field = json_object_get_field_by_name(torrent_item_obj, "percentDone");
- if(!id_field || id_field->type != json_type_number
- || !name_field || name_field->type != json_type_string
- || !percent_done_field || percent_done_field->type != json_type_number)
- {
+ cJSON *id_field = cJSON_GetObjectItemCaseSensitive(torrent_item, "id");
+ cJSON *name_field = cJSON_GetObjectItemCaseSensitive(torrent_item, "name");
+ cJSON *percent_done_field = cJSON_GetObjectItemCaseSensitive(torrent_item, "percentDone");
+ if(!cJSON_IsNumber(id_field) || !cJSON_IsString(name_field) || !cJSON_IsNumber(percent_done_field))
continue;
- }
-
- callback(atoi(json_value_as_number(id_field)->number),
- json_value_as_string(name_field)->string,
- atof(json_value_as_number(percent_done_field)->number),
- userdata);
- torrent_item = torrent_item->next;
+ callback(id_field->valuedouble, name_field->valuestring, percent_done_field->valuedouble, userdata);
}
cleanup:
- free(json_response_val);
+ cJSON_Delete(json_response);
buffer_deinit(&buffer);
return result;
}