diff options
-rwxr-xr-x | automedia | bin | 116608 -> 108448 bytes | |||
-rwxr-xr-x | build.sh | 4 | ||||
-rw-r--r-- | depends/cJSON.c | 3110 | ||||
-rw-r--r-- | depends/cJSON.h | 293 | ||||
-rw-r--r-- | src/html.c | 66 | ||||
-rw-r--r-- | src/html.h | 4 | ||||
-rw-r--r-- | src/json.h | 3077 | ||||
-rw-r--r-- | src/main.c | 67 | ||||
-rw-r--r-- | src/rss.c | 36 | ||||
-rw-r--r-- | src/rss.h | 4 | ||||
-rw-r--r-- | src/rss_html_common.c | 368 | ||||
-rw-r--r-- | src/rss_html_common.h | 9 | ||||
-rw-r--r-- | src/transmission.c | 118 |
13 files changed, 3582 insertions, 3574 deletions
Binary files differ @@ -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 @@ -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; @@ -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. */ @@ -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; @@ -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; } @@ -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; } |