| From be2cc81e58d33e09aef534d517c5dda0d7f12272 Mon Sep 17 00:00:00 2001 |
| From: Serge Bazanski <serge@monogon.tech> |
| Date: Tue, 4 Jun 2024 12:49:01 +0200 |
| Subject: [PATCH 3/4] swtpm_setup: replace dep on JSON-GLib with |
| sheredom/json.h |
| |
| Turns out swtpm_setup requests capability info from `swtpm` by shelling |
| out to it then parsing JSON output. |
| |
| Pulling in JSON-Glib as a library sounds like pain. Let's just use a |
| simple single-file JSON parser library. |
| |
| There will be no comment on using JSON for this from me. |
| --- |
| include/json.h | 3462 +++++++++++++++++++++++++++++++++ |
| src/swtpm_setup/swtpm_setup.c | 150 +- |
| 2 files changed, 3554 insertions(+), 58 deletions(-) |
| create mode 100644 include/json.h |
| |
| diff --git a/include/json.h b/include/json.h |
| new file mode 100644 |
| index 0000000..7116d85 |
| --- /dev/null |
| +++ b/include/json.h |
| @@ -0,0 +1,3462 @@ |
| +/* |
| + 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 warning: no function prototype given: converting '()' to '(void)' */ |
| +#pragma warning(disable : 4255) |
| + |
| +/* disable warning: '__cplusplus' is not defined as a preprocessor macro, |
| + * replacing with '0' for '#if/#elif' */ |
| +#pragma warning(disable : 4668) |
| + |
| +/* disable warning: 'bytes padding added after construct' */ |
| +#pragma warning(disable : 4820) |
| +#endif |
| + |
| +#include <stddef.h> |
| +#include <string.h> |
| + |
| +#if defined(__TINYC__) |
| +#define JSON_ATTRIBUTE(a) __attribute((a)) |
| +#else |
| +#define JSON_ATTRIBUTE(a) __attribute__((a)) |
| +#endif |
| + |
| +#if defined(_MSC_VER) || defined(__WATCOMC__) |
| +#define json_weak __inline |
| +#elif defined(__clang__) || defined(__GNUC__) || defined(__TINYC__) |
| +#define json_weak JSON_ATTRIBUTE(weak) |
| +#else |
| +#error Non clang, non gcc, non MSVC, non tcc, non WATCOM 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 alloc_func_ptr 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. If |
| + * alloc_func_ptr is null then malloc is used. */ |
| +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); |
| + |
| +/* Extracts a value and all the data that makes it up into a newly created |
| + * value. json_extract_value performs 1 call to malloc for the entire encoding. |
| + */ |
| +json_weak struct json_value_s * |
| +json_extract_value(const struct json_value_s *value); |
| + |
| +/* Extracts a value and all the data that makes it up into a newly created |
| + * value. json_extract_value performs 1 call to alloc_func_ptr for the entire |
| + * encoding. If alloc_func_ptr is null then malloc is used. */ |
| +json_weak struct json_value_s * |
| +json_extract_value_ex(const struct json_value_s *value, |
| + void *(*alloc_func_ptr)(void *, size_t), void *user_data); |
| + |
| +/* 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. */ |
| +typedef 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 |
| + |
| +} json_type_t; |
| + |
| +/* A JSON string value. */ |
| +typedef struct json_string_s { |
| + /* utf-8 string */ |
| + const char *string; |
| + /* The size (in bytes) of the string */ |
| + size_t string_size; |
| + |
| +} json_string_t; |
| + |
| +/* A JSON string value (extended). */ |
| +typedef 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; |
| + |
| +} json_string_ex_t; |
| + |
| +/* A JSON number value. */ |
| +typedef 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; |
| + |
| +} json_number_t; |
| + |
| +/* an element of a JSON object. */ |
| +typedef 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; |
| + |
| +} json_object_element_t; |
| + |
| +/* a JSON object value. */ |
| +typedef 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; |
| + |
| +} json_object_t; |
| + |
| +/* an element of a JSON array. */ |
| +typedef 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; |
| + |
| +} json_array_element_t; |
| + |
| +/* a JSON array value. */ |
| +typedef 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; |
| + |
| +} json_array_t; |
| + |
| +/* a JSON value. */ |
| +typedef 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; |
| + |
| +} json_value_t; |
| + |
| +/* a JSON value (extended). */ |
| +typedef 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; |
| + |
| +} json_value_ex_t; |
| + |
| +/* 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(). */ |
| +typedef 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; |
| + |
| +} json_parse_result_t; |
| + |
| +#ifdef __cplusplus |
| +} /* extern "C". */ |
| +#endif |
| + |
| +#include <stdlib.h> |
| + |
| +#if defined(_MSC_VER) |
| +#pragma warning(pop) |
| +#endif |
| + |
| +#if defined(_MSC_VER) && (_MSC_VER < 1920) |
| +#define json_uintmax_t unsigned __int64 |
| +#else |
| +#include <inttypes.h> |
| +#define json_uintmax_t uintmax_t |
| +#endif |
| + |
| +#if defined(_MSC_VER) |
| +#define json_strtoumax _strtoui64 |
| +#else |
| +#define json_strtoumax strtoumax |
| +#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" |
| + |
| +#if __has_warning("-Wunsafe-buffer-usage") |
| +#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" |
| +#endif |
| + |
| +#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; |
| + |
| + if (offset >= state->size) { |
| + return 0; |
| + } |
| + |
| + /* 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) { |
| + /* to have a C-style comment we need at least 2 characters of space */ |
| + if ((state->offset + 2) > state->size) { |
| + return 0; |
| + } |
| + |
| + /* do we have a comment? */ |
| + if ('/' == state->src[state->offset]) { |
| + if ('/' == state->src[state->offset + 1]) { |
| + /* we had a comment of the form // */ |
| + |
| + /* skip first '/' */ |
| + state->offset++; |
| + |
| + /* 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 + 1]) { |
| + /* we had a comment in the C-style long form */ |
| + |
| + /* skip '/' */ |
| + state->offset++; |
| + |
| + /* 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++; |
| + |
| + switch (src[offset]) { |
| + default: |
| + break; |
| + case '\0': |
| + case '\t': |
| + state->error = json_parse_error_invalid_string; |
| + state->offset = offset; |
| + return 1; |
| + } |
| + |
| + 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++; |
| + } |
| + } |
| + |
| + /* If the offset is equal to the size, we had a non-terminated string! */ |
| + if (offset == size) { |
| + state->error = json_parse_error_premature_end_of_buffer; |
| + state->offset = offset - 1; |
| + return 1; |
| + } |
| + |
| + /* 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; |
| + int found_closing_brace = 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); |
| + |
| + if ((state->offset == size) && !is_global_object) { |
| + state->error = json_parse_error_premature_end_of_buffer; |
| + return 1; |
| + } |
| + |
| + do { |
| + 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++; |
| + |
| + found_closing_brace = 1; |
| + |
| + /* 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 one 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; |
| + } while (state->offset < size); |
| + |
| + if ((state->offset == size) && !is_global_object && !found_closing_brace) { |
| + state->error = json_parse_error_premature_end_of_buffer; |
| + return 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 (inf_or_nan) { |
| + if (offset < size) { |
| + switch (src[offset]) { |
| + default: |
| + break; |
| + case '0': |
| + case '1': |
| + case '2': |
| + case '3': |
| + case '4': |
| + case '5': |
| + case '6': |
| + case '7': |
| + case '8': |
| + case '9': |
| + case 'e': |
| + case 'E': |
| + /* cannot follow an inf or nan with digits! */ |
| + state->error = json_parse_error_invalid_number_format; |
| + state->offset = offset; |
| + return 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 ((offset >= size) || !('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! Skip 'e' or 'E'. */ |
| + offset++; |
| + |
| + if ((offset < size) && ('-' == src[offset] || '+' == src[offset])) { |
| + /* skip optional '-' or '+'. */ |
| + offset++; |
| + } |
| + |
| + if ((offset < size) && !('0' <= src[offset] && src[offset] <= '9')) { |
| + /* an exponent must have at least one digit! */ |
| + state->error = json_parse_error_invalid_number_format; |
| + state->offset = offset; |
| + return 1; |
| + } |
| + |
| + /* consume exponent digits. */ |
| + do { |
| + offset++; |
| + } while ((offset < size) && ('0' <= src[offset] && src[offset] <= '9')); |
| + } |
| + } |
| + |
| + 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_extract_result_s { |
| + size_t dom_size; |
| + size_t data_size; |
| +}; |
| + |
| +struct json_value_s *json_extract_value(const struct json_value_s *value) { |
| + return json_extract_value_ex(value, json_null, json_null); |
| +} |
| + |
| +json_weak struct json_extract_result_s |
| +json_extract_get_number_size(const struct json_number_s *const number); |
| +json_weak struct json_extract_result_s |
| +json_extract_get_string_size(const struct json_string_s *const string); |
| +json_weak struct json_extract_result_s |
| +json_extract_get_object_size(const struct json_object_s *const object); |
| +json_weak struct json_extract_result_s |
| +json_extract_get_array_size(const struct json_array_s *const array); |
| +json_weak struct json_extract_result_s |
| +json_extract_get_value_size(const struct json_value_s *const value); |
| + |
| +struct json_extract_result_s |
| +json_extract_get_number_size(const struct json_number_s *const number) { |
| + struct json_extract_result_s result; |
| + result.dom_size = sizeof(struct json_number_s); |
| + result.data_size = number->number_size; |
| + return result; |
| +} |
| + |
| +struct json_extract_result_s |
| +json_extract_get_string_size(const struct json_string_s *const string) { |
| + struct json_extract_result_s result; |
| + result.dom_size = sizeof(struct json_string_s); |
| + result.data_size = string->string_size + 1; |
| + return result; |
| +} |
| + |
| +struct json_extract_result_s |
| +json_extract_get_object_size(const struct json_object_s *const object) { |
| + struct json_extract_result_s result; |
| + size_t i; |
| + const struct json_object_element_s *element = object->start; |
| + |
| + result.dom_size = sizeof(struct json_object_s) + |
| + (sizeof(struct json_object_element_s) * object->length); |
| + result.data_size = 0; |
| + |
| + for (i = 0; i < object->length; i++) { |
| + const struct json_extract_result_s string_result = |
| + json_extract_get_string_size(element->name); |
| + const struct json_extract_result_s value_result = |
| + json_extract_get_value_size(element->value); |
| + |
| + result.dom_size += string_result.dom_size; |
| + result.data_size += string_result.data_size; |
| + |
| + result.dom_size += value_result.dom_size; |
| + result.data_size += value_result.data_size; |
| + |
| + element = element->next; |
| + } |
| + |
| + return result; |
| +} |
| + |
| +struct json_extract_result_s |
| +json_extract_get_array_size(const struct json_array_s *const array) { |
| + struct json_extract_result_s result; |
| + size_t i; |
| + const struct json_array_element_s *element = array->start; |
| + |
| + result.dom_size = sizeof(struct json_array_s) + |
| + (sizeof(struct json_array_element_s) * array->length); |
| + result.data_size = 0; |
| + |
| + for (i = 0; i < array->length; i++) { |
| + const struct json_extract_result_s value_result = |
| + json_extract_get_value_size(element->value); |
| + |
| + result.dom_size += value_result.dom_size; |
| + result.data_size += value_result.data_size; |
| + |
| + element = element->next; |
| + } |
| + |
| + return result; |
| +} |
| + |
| +struct json_extract_result_s |
| +json_extract_get_value_size(const struct json_value_s *const value) { |
| + struct json_extract_result_s result = {0, 0}; |
| + |
| + switch (value->type) { |
| + default: |
| + break; |
| + case json_type_object: |
| + result = json_extract_get_object_size( |
| + (const struct json_object_s *)value->payload); |
| + break; |
| + case json_type_array: |
| + result = json_extract_get_array_size( |
| + (const struct json_array_s *)value->payload); |
| + break; |
| + case json_type_number: |
| + result = json_extract_get_number_size( |
| + (const struct json_number_s *)value->payload); |
| + break; |
| + case json_type_string: |
| + result = json_extract_get_string_size( |
| + (const struct json_string_s *)value->payload); |
| + break; |
| + } |
| + |
| + result.dom_size += sizeof(struct json_value_s); |
| + |
| + return result; |
| +} |
| + |
| +struct json_extract_state_s { |
| + char *dom; |
| + char *data; |
| +}; |
| + |
| +json_weak void json_extract_copy_value(struct json_extract_state_s *const state, |
| + const struct json_value_s *const value); |
| +void json_extract_copy_value(struct json_extract_state_s *const state, |
| + const struct json_value_s *const value) { |
| + struct json_string_s *string; |
| + struct json_number_s *number; |
| + struct json_object_s *object; |
| + struct json_array_s *array; |
| + struct json_value_s *new_value; |
| + |
| + memcpy(state->dom, value, sizeof(struct json_value_s)); |
| + new_value = (struct json_value_s *)state->dom; |
| + state->dom += sizeof(struct json_value_s); |
| + new_value->payload = state->dom; |
| + |
| + if (json_type_string == value->type) { |
| + memcpy(state->dom, value->payload, sizeof(struct json_string_s)); |
| + string = (struct json_string_s *)state->dom; |
| + state->dom += sizeof(struct json_string_s); |
| + |
| + memcpy(state->data, string->string, string->string_size + 1); |
| + string->string = state->data; |
| + state->data += string->string_size + 1; |
| + } else if (json_type_number == value->type) { |
| + memcpy(state->dom, value->payload, sizeof(struct json_number_s)); |
| + number = (struct json_number_s *)state->dom; |
| + state->dom += sizeof(struct json_number_s); |
| + |
| + memcpy(state->data, number->number, number->number_size); |
| + number->number = state->data; |
| + state->data += number->number_size; |
| + } else if (json_type_object == value->type) { |
| + struct json_object_element_s *element; |
| + size_t i; |
| + |
| + memcpy(state->dom, value->payload, sizeof(struct json_object_s)); |
| + object = (struct json_object_s *)state->dom; |
| + state->dom += sizeof(struct json_object_s); |
| + |
| + element = object->start; |
| + object->start = (struct json_object_element_s *)state->dom; |
| + |
| + for (i = 0; i < object->length; i++) { |
| + struct json_value_s *previous_value; |
| + struct json_object_element_s *previous_element; |
| + |
| + memcpy(state->dom, element, sizeof(struct json_object_element_s)); |
| + element = (struct json_object_element_s *)state->dom; |
| + state->dom += sizeof(struct json_object_element_s); |
| + |
| + string = element->name; |
| + memcpy(state->dom, string, sizeof(struct json_string_s)); |
| + string = (struct json_string_s *)state->dom; |
| + state->dom += sizeof(struct json_string_s); |
| + element->name = string; |
| + |
| + memcpy(state->data, string->string, string->string_size + 1); |
| + string->string = state->data; |
| + state->data += string->string_size + 1; |
| + |
| + previous_value = element->value; |
| + element->value = (struct json_value_s *)state->dom; |
| + json_extract_copy_value(state, previous_value); |
| + |
| + previous_element = element; |
| + element = element->next; |
| + |
| + if (element) { |
| + previous_element->next = (struct json_object_element_s *)state->dom; |
| + } |
| + } |
| + } else if (json_type_array == value->type) { |
| + struct json_array_element_s *element; |
| + size_t i; |
| + |
| + memcpy(state->dom, value->payload, sizeof(struct json_array_s)); |
| + array = (struct json_array_s *)state->dom; |
| + state->dom += sizeof(struct json_array_s); |
| + |
| + element = array->start; |
| + array->start = (struct json_array_element_s *)state->dom; |
| + |
| + for (i = 0; i < array->length; i++) { |
| + struct json_value_s *previous_value; |
| + struct json_array_element_s *previous_element; |
| + |
| + memcpy(state->dom, element, sizeof(struct json_array_element_s)); |
| + element = (struct json_array_element_s *)state->dom; |
| + state->dom += sizeof(struct json_array_element_s); |
| + |
| + previous_value = element->value; |
| + element->value = (struct json_value_s *)state->dom; |
| + json_extract_copy_value(state, previous_value); |
| + |
| + previous_element = element; |
| + element = element->next; |
| + |
| + if (element) { |
| + previous_element->next = (struct json_array_element_s *)state->dom; |
| + } |
| + } |
| + } |
| +} |
| + |
| +struct json_value_s *json_extract_value_ex(const struct json_value_s *value, |
| + void *(*alloc_func_ptr)(void *, |
| + size_t), |
| + void *user_data) { |
| + void *allocation; |
| + struct json_extract_result_s result; |
| + struct json_extract_state_s state; |
| + size_t total_size; |
| + |
| + if (json_null == value) { |
| + /* invalid value was null! */ |
| + return json_null; |
| + } |
| + |
| + result = json_extract_get_value_size(value); |
| + total_size = result.dom_size + result.data_size; |
| + |
| + if (json_null == alloc_func_ptr) { |
| + allocation = malloc(total_size); |
| + } else { |
| + allocation = alloc_func_ptr(user_data, total_size); |
| + } |
| + |
| + state.dom = (char *)allocation; |
| + state.data = state.dom + result.dom_size; |
| + |
| + json_extract_copy_value(&state, value); |
| + |
| + return (struct json_value_s *)allocation; |
| +} |
| + |
| +struct json_string_s *json_value_as_string(struct json_value_s *const value) { |
| + if (value->type != json_type_string) { |
| + return json_null; |
| + } |
| + |
| + return (struct json_string_s *)value->payload; |
| +} |
| + |
| +struct json_number_s *json_value_as_number(struct json_value_s *const value) { |
| + if (value->type != json_type_number) { |
| + return json_null; |
| + } |
| + |
| + return (struct json_number_s *)value->payload; |
| +} |
| + |
| +struct json_object_s *json_value_as_object(struct json_value_s *const value) { |
| + if (value->type != json_type_object) { |
| + return json_null; |
| + } |
| + |
| + return (struct json_object_s *)value->payload; |
| +} |
| + |
| +struct json_array_s *json_value_as_array(struct json_value_s *const value) { |
| + if (value->type != json_type_array) { |
| + return json_null; |
| + } |
| + |
| + return (struct json_array_s *)value->payload; |
| +} |
| + |
| +int json_value_is_true(const struct json_value_s *const value) { |
| + return value->type == json_type_true; |
| +} |
| + |
| +int json_value_is_false(const struct json_value_s *const value) { |
| + return value->type == json_type_false; |
| +} |
| + |
| +int json_value_is_null(const struct json_value_s *const value) { |
| + return value->type == json_type_null; |
| +} |
| + |
| +json_weak int |
| +json_write_minified_get_value_size(const struct json_value_s *value, |
| + size_t *size); |
| + |
| +json_weak int json_write_get_number_size(const struct json_number_s *number, |
| + size_t *size); |
| +int json_write_get_number_size(const struct json_number_s *number, |
| + size_t *size) { |
| + json_uintmax_t parsed_number; |
| + size_t i; |
| + |
| + if (number->number_size >= 2) { |
| + switch (number->number[1]) { |
| + default: |
| + break; |
| + case 'x': |
| + case 'X': |
| + /* the number is a json_parse_flags_allow_hexadecimal_numbers hexadecimal |
| + * so we have to do extra work to convert it to a non-hexadecimal for JSON |
| + * output. */ |
| + parsed_number = json_strtoumax(number->number, json_null, 0); |
| + |
| + i = 0; |
| + |
| + while (0 != parsed_number) { |
| + parsed_number /= 10; |
| + i++; |
| + } |
| + |
| + *size += i; |
| + return 0; |
| + } |
| + } |
| + |
| + /* check to see if the number has leading/trailing decimal point. */ |
| + i = 0; |
| + |
| + /* skip any leading '+' or '-'. */ |
| + if ((i < number->number_size) && |
| + (('+' == number->number[i]) || ('-' == number->number[i]))) { |
| + i++; |
| + } |
| + |
| + /* check if we have infinity. */ |
| + if ((i < number->number_size) && ('I' == number->number[i])) { |
| + const char *inf = "Infinity"; |
| + size_t k; |
| + |
| + for (k = i; k < number->number_size; k++) { |
| + const char c = *inf++; |
| + |
| + /* Check if we found the Infinity string! */ |
| + if ('\0' == c) { |
| + break; |
| + } else if (c != number->number[k]) { |
| + break; |
| + } |
| + } |
| + |
| + if ('\0' == *inf) { |
| + /* Inf becomes 1.7976931348623158e308 because JSON can't support it. */ |
| + *size += 22; |
| + |
| + /* if we had a leading '-' we need to record it in the JSON output. */ |
| + if ('-' == number->number[0]) { |
| + *size += 1; |
| + } |
| + } |
| + |
| + return 0; |
| + } |
| + |
| + /* check if we have nan. */ |
| + if ((i < number->number_size) && ('N' == number->number[i])) { |
| + const char *nan = "NaN"; |
| + size_t k; |
| + |
| + for (k = i; k < number->number_size; k++) { |
| + const char c = *nan++; |
| + |
| + /* Check if we found the NaN string! */ |
| + if ('\0' == c) { |
| + break; |
| + } else if (c != number->number[k]) { |
| + break; |
| + } |
| + } |
| + |
| + if ('\0' == *nan) { |
| + /* NaN becomes 1 because JSON can't support it. */ |
| + *size += 1; |
| + |
| + return 0; |
| + } |
| + } |
| + |
| + /* if we had a leading decimal point. */ |
| + if ((i < number->number_size) && ('.' == number->number[i])) { |
| + /* 1 + because we had a leading decimal point. */ |
| + *size += 1; |
| + goto cleanup; |
| + } |
| + |
| + for (; i < number->number_size; i++) { |
| + const char c = number->number[i]; |
| + if (!('0' <= c && c <= '9')) { |
| + break; |
| + } |
| + } |
| + |
| + /* if we had a trailing decimal point. */ |
| + if ((i + 1 == number->number_size) && ('.' == number->number[i])) { |
| + /* 1 + because we had a trailing decimal point. */ |
| + *size += 1; |
| + goto cleanup; |
| + } |
| + |
| +cleanup: |
| + *size += number->number_size; /* the actual string of the number. */ |
| + |
| + /* if we had a leading '+' we don't record it in the JSON output. */ |
| + if ('+' == number->number[0]) { |
| + *size -= 1; |
| + } |
| + |
| + return 0; |
| +} |
| + |
| +json_weak int json_write_get_string_size(const struct json_string_s *string, |
| + size_t *size); |
| +int json_write_get_string_size(const struct json_string_s *string, |
| + size_t *size) { |
| + size_t i; |
| + for (i = 0; i < string->string_size; i++) { |
| + switch (string->string[i]) { |
| + case '"': |
| + case '\\': |
| + case '\b': |
| + case '\f': |
| + case '\n': |
| + case '\r': |
| + case '\t': |
| + *size += 2; |
| + break; |
| + default: |
| + *size += 1; |
| + break; |
| + } |
| + } |
| + |
| + *size += 2; /* need to encode the surrounding '"' characters. */ |
| + |
| + return 0; |
| +} |
| + |
| +json_weak int |
| +json_write_minified_get_array_size(const struct json_array_s *array, |
| + size_t *size); |
| +int json_write_minified_get_array_size(const struct json_array_s *array, |
| + size_t *size) { |
| + struct json_array_element_s *element; |
| + |
| + *size += 2; /* '[' and ']'. */ |
| + |
| + if (1 < array->length) { |
| + *size += array->length - 1; /* ','s seperate each element. */ |
| + } |
| + |
| + for (element = array->start; json_null != element; element = element->next) { |
| + if (json_write_minified_get_value_size(element->value, size)) { |
| + /* value was malformed! */ |
| + return 1; |
| + } |
| + } |
| + |
| + return 0; |
| +} |
| + |
| +json_weak int |
| +json_write_minified_get_object_size(const struct json_object_s *object, |
| + size_t *size); |
| +int json_write_minified_get_object_size(const struct json_object_s *object, |
| + size_t *size) { |
| + struct json_object_element_s *element; |
| + |
| + *size += 2; /* '{' and '}'. */ |
| + |
| + *size += object->length; /* ':'s seperate each name/value pair. */ |
| + |
| + if (1 < object->length) { |
| + *size += object->length - 1; /* ','s seperate each element. */ |
| + } |
| + |
| + for (element = object->start; json_null != element; element = element->next) { |
| + if (json_write_get_string_size(element->name, size)) { |
| + /* string was malformed! */ |
| + return 1; |
| + } |
| + |
| + if (json_write_minified_get_value_size(element->value, size)) { |
| + /* value was malformed! */ |
| + return 1; |
| + } |
| + } |
| + |
| + return 0; |
| +} |
| + |
| +json_weak int |
| +json_write_minified_get_value_size(const struct json_value_s *value, |
| + size_t *size); |
| +int json_write_minified_get_value_size(const struct json_value_s *value, |
| + size_t *size) { |
| + switch (value->type) { |
| + default: |
| + /* unknown value type found! */ |
| + return 1; |
| + case json_type_number: |
| + return json_write_get_number_size((struct json_number_s *)value->payload, |
| + size); |
| + case json_type_string: |
| + return json_write_get_string_size((struct json_string_s *)value->payload, |
| + size); |
| + case json_type_array: |
| + return json_write_minified_get_array_size( |
| + (struct json_array_s *)value->payload, size); |
| + case json_type_object: |
| + return json_write_minified_get_object_size( |
| + (struct json_object_s *)value->payload, size); |
| + case json_type_true: |
| + *size += 4; /* the string "true". */ |
| + return 0; |
| + case json_type_false: |
| + *size += 5; /* the string "false". */ |
| + return 0; |
| + case json_type_null: |
| + *size += 4; /* the string "null". */ |
| + return 0; |
| + } |
| +} |
| + |
| +json_weak char *json_write_minified_value(const struct json_value_s *value, |
| + char *data); |
| + |
| +json_weak char *json_write_number(const struct json_number_s *number, |
| + char *data); |
| +char *json_write_number(const struct json_number_s *number, char *data) { |
| + json_uintmax_t parsed_number, backup; |
| + size_t i; |
| + |
| + if (number->number_size >= 2) { |
| + switch (number->number[1]) { |
| + default: |
| + break; |
| + case 'x': |
| + case 'X': |
| + /* The number is a json_parse_flags_allow_hexadecimal_numbers hexadecimal |
| + * so we have to do extra work to convert it to a non-hexadecimal for JSON |
| + * output. */ |
| + parsed_number = json_strtoumax(number->number, json_null, 0); |
| + |
| + /* We need a copy of parsed number twice, so take a backup of it. */ |
| + backup = parsed_number; |
| + |
| + i = 0; |
| + |
| + while (0 != parsed_number) { |
| + parsed_number /= 10; |
| + i++; |
| + } |
| + |
| + /* Restore parsed_number to its original value stored in the backup. */ |
| + parsed_number = backup; |
| + |
| + /* Now use backup to take a copy of i, or the length of the string. */ |
| + backup = i; |
| + |
| + do { |
| + *(data + i - 1) = '0' + (char)(parsed_number % 10); |
| + parsed_number /= 10; |
| + i--; |
| + } while (0 != parsed_number); |
| + |
| + data += backup; |
| + |
| + return data; |
| + } |
| + } |
| + |
| + /* check to see if the number has leading/trailing decimal point. */ |
| + i = 0; |
| + |
| + /* skip any leading '-'. */ |
| + if ((i < number->number_size) && |
| + (('+' == number->number[i]) || ('-' == number->number[i]))) { |
| + i++; |
| + } |
| + |
| + /* check if we have infinity. */ |
| + if ((i < number->number_size) && ('I' == number->number[i])) { |
| + const char *inf = "Infinity"; |
| + size_t k; |
| + |
| + for (k = i; k < number->number_size; k++) { |
| + const char c = *inf++; |
| + |
| + /* Check if we found the Infinity string! */ |
| + if ('\0' == c) { |
| + break; |
| + } else if (c != number->number[k]) { |
| + break; |
| + } |
| + } |
| + |
| + if ('\0' == *inf++) { |
| + const char *dbl_max; |
| + |
| + /* if we had a leading '-' we need to record it in the JSON output. */ |
| + if ('-' == number->number[0]) { |
| + *data++ = '-'; |
| + } |
| + |
| + /* Inf becomes 1.7976931348623158e308 because JSON can't support it. */ |
| + for (dbl_max = "1.7976931348623158e308"; '\0' != *dbl_max; dbl_max++) { |
| + *data++ = *dbl_max; |
| + } |
| + |
| + return data; |
| + } |
| + } |
| + |
| + /* check if we have nan. */ |
| + if ((i < number->number_size) && ('N' == number->number[i])) { |
| + const char *nan = "NaN"; |
| + size_t k; |
| + |
| + for (k = i; k < number->number_size; k++) { |
| + const char c = *nan++; |
| + |
| + /* Check if we found the NaN string! */ |
| + if ('\0' == c) { |
| + break; |
| + } else if (c != number->number[k]) { |
| + break; |
| + } |
| + } |
| + |
| + if ('\0' == *nan++) { |
| + /* NaN becomes 0 because JSON can't support it. */ |
| + *data++ = '0'; |
| + return data; |
| + } |
| + } |
| + |
| + /* if we had a leading decimal point. */ |
| + if ((i < number->number_size) && ('.' == number->number[i])) { |
| + i = 0; |
| + |
| + /* skip any leading '+'. */ |
| + if ('+' == number->number[i]) { |
| + i++; |
| + } |
| + |
| + /* output the leading '-' if we had one. */ |
| + if ('-' == number->number[i]) { |
| + *data++ = '-'; |
| + i++; |
| + } |
| + |
| + /* insert a '0' to fix the leading decimal point for JSON output. */ |
| + *data++ = '0'; |
| + |
| + /* and output the rest of the number as normal. */ |
| + for (; i < number->number_size; i++) { |
| + *data++ = number->number[i]; |
| + } |
| + |
| + return data; |
| + } |
| + |
| + for (; i < number->number_size; i++) { |
| + const char c = number->number[i]; |
| + if (!('0' <= c && c <= '9')) { |
| + break; |
| + } |
| + } |
| + |
| + /* if we had a trailing decimal point. */ |
| + if ((i + 1 == number->number_size) && ('.' == number->number[i])) { |
| + i = 0; |
| + |
| + /* skip any leading '+'. */ |
| + if ('+' == number->number[i]) { |
| + i++; |
| + } |
| + |
| + /* output the leading '-' if we had one. */ |
| + if ('-' == number->number[i]) { |
| + *data++ = '-'; |
| + i++; |
| + } |
| + |
| + /* and output the rest of the number as normal. */ |
| + for (; i < number->number_size; i++) { |
| + *data++ = number->number[i]; |
| + } |
| + |
| + /* insert a '0' to fix the trailing decimal point for JSON output. */ |
| + *data++ = '0'; |
| + |
| + return data; |
| + } |
| + |
| + i = 0; |
| + |
| + /* skip any leading '+'. */ |
| + if ('+' == number->number[i]) { |
| + i++; |
| + } |
| + |
| + for (; i < number->number_size; i++) { |
| + *data++ = number->number[i]; |
| + } |
| + |
| + return data; |
| +} |
| + |
| +json_weak char *json_write_string(const struct json_string_s *string, |
| + char *data); |
| +char *json_write_string(const struct json_string_s *string, char *data) { |
| + size_t i; |
| + |
| + *data++ = '"'; /* open the string. */ |
| + |
| + for (i = 0; i < string->string_size; i++) { |
| + switch (string->string[i]) { |
| + case '"': |
| + *data++ = '\\'; /* escape the control character. */ |
| + *data++ = '"'; |
| + break; |
| + case '\\': |
| + *data++ = '\\'; /* escape the control character. */ |
| + *data++ = '\\'; |
| + break; |
| + case '\b': |
| + *data++ = '\\'; /* escape the control character. */ |
| + *data++ = 'b'; |
| + break; |
| + case '\f': |
| + *data++ = '\\'; /* escape the control character. */ |
| + *data++ = 'f'; |
| + break; |
| + case '\n': |
| + *data++ = '\\'; /* escape the control character. */ |
| + *data++ = 'n'; |
| + break; |
| + case '\r': |
| + *data++ = '\\'; /* escape the control character. */ |
| + *data++ = 'r'; |
| + break; |
| + case '\t': |
| + *data++ = '\\'; /* escape the control character. */ |
| + *data++ = 't'; |
| + break; |
| + default: |
| + *data++ = string->string[i]; |
| + break; |
| + } |
| + } |
| + |
| + *data++ = '"'; /* close the string. */ |
| + |
| + return data; |
| +} |
| + |
| +json_weak char *json_write_minified_array(const struct json_array_s *array, |
| + char *data); |
| +char *json_write_minified_array(const struct json_array_s *array, char *data) { |
| + struct json_array_element_s *element = json_null; |
| + |
| + *data++ = '['; /* open the array. */ |
| + |
| + for (element = array->start; json_null != element; element = element->next) { |
| + if (element != array->start) { |
| + *data++ = ','; /* ','s seperate each element. */ |
| + } |
| + |
| + data = json_write_minified_value(element->value, data); |
| + |
| + if (json_null == data) { |
| + /* value was malformed! */ |
| + return json_null; |
| + } |
| + } |
| + |
| + *data++ = ']'; /* close the array. */ |
| + |
| + return data; |
| +} |
| + |
| +json_weak char *json_write_minified_object(const struct json_object_s *object, |
| + char *data); |
| +char *json_write_minified_object(const struct json_object_s *object, |
| + char *data) { |
| + struct json_object_element_s *element = json_null; |
| + |
| + *data++ = '{'; /* open the object. */ |
| + |
| + for (element = object->start; json_null != element; element = element->next) { |
| + if (element != object->start) { |
| + *data++ = ','; /* ','s seperate each element. */ |
| + } |
| + |
| + data = json_write_string(element->name, data); |
| + |
| + if (json_null == data) { |
| + /* string was malformed! */ |
| + return json_null; |
| + } |
| + |
| + *data++ = ':'; /* ':'s seperate each name/value pair. */ |
| + |
| + data = json_write_minified_value(element->value, data); |
| + |
| + if (json_null == data) { |
| + /* value was malformed! */ |
| + return json_null; |
| + } |
| + } |
| + |
| + *data++ = '}'; /* close the object. */ |
| + |
| + return data; |
| +} |
| + |
| +json_weak char *json_write_minified_value(const struct json_value_s *value, |
| + char *data); |
| +char *json_write_minified_value(const struct json_value_s *value, char *data) { |
| + switch (value->type) { |
| + default: |
| + /* unknown value type found! */ |
| + return json_null; |
| + case json_type_number: |
| + return json_write_number((struct json_number_s *)value->payload, data); |
| + case json_type_string: |
| + return json_write_string((struct json_string_s *)value->payload, data); |
| + case json_type_array: |
| + return json_write_minified_array((struct json_array_s *)value->payload, |
| + data); |
| + case json_type_object: |
| + return json_write_minified_object((struct json_object_s *)value->payload, |
| + data); |
| + case json_type_true: |
| + data[0] = 't'; |
| + data[1] = 'r'; |
| + data[2] = 'u'; |
| + data[3] = 'e'; |
| + return data + 4; |
| + case json_type_false: |
| + data[0] = 'f'; |
| + data[1] = 'a'; |
| + data[2] = 'l'; |
| + data[3] = 's'; |
| + data[4] = 'e'; |
| + return data + 5; |
| + case json_type_null: |
| + data[0] = 'n'; |
| + data[1] = 'u'; |
| + data[2] = 'l'; |
| + data[3] = 'l'; |
| + return data + 4; |
| + } |
| +} |
| + |
| +void *json_write_minified(const struct json_value_s *value, size_t *out_size) { |
| + size_t size = 0; |
| + char *data = json_null; |
| + char *data_end = json_null; |
| + |
| + if (json_null == value) { |
| + return json_null; |
| + } |
| + |
| + if (json_write_minified_get_value_size(value, &size)) { |
| + /* value was malformed! */ |
| + return json_null; |
| + } |
| + |
| + size += 1; /* for the '\0' null terminating character. */ |
| + |
| + data = (char *)malloc(size); |
| + |
| + if (json_null == data) { |
| + /* malloc failed! */ |
| + return json_null; |
| + } |
| + |
| + data_end = json_write_minified_value(value, data); |
| + |
| + if (json_null == data_end) { |
| + /* bad chi occurred! */ |
| + free(data); |
| + return json_null; |
| + } |
| + |
| + /* null terminated the string. */ |
| + *data_end = '\0'; |
| + |
| + if (json_null != out_size) { |
| + *out_size = size; |
| + } |
| + |
| + return data; |
| +} |
| + |
| +json_weak int json_write_pretty_get_value_size(const struct json_value_s *value, |
| + size_t depth, size_t indent_size, |
| + size_t newline_size, |
| + size_t *size); |
| + |
| +json_weak int json_write_pretty_get_array_size(const struct json_array_s *array, |
| + size_t depth, size_t indent_size, |
| + size_t newline_size, |
| + size_t *size); |
| +int json_write_pretty_get_array_size(const struct json_array_s *array, |
| + size_t depth, size_t indent_size, |
| + size_t newline_size, size_t *size) { |
| + struct json_array_element_s *element; |
| + |
| + *size += 1; /* '['. */ |
| + |
| + if (0 < array->length) { |
| + /* if we have any elements we need to add a newline after our '['. */ |
| + *size += newline_size; |
| + |
| + *size += array->length - 1; /* ','s seperate each element. */ |
| + |
| + for (element = array->start; json_null != element; |
| + element = element->next) { |
| + /* each element gets an indent. */ |
| + *size += (depth + 1) * indent_size; |
| + |
| + if (json_write_pretty_get_value_size(element->value, depth + 1, |
| + indent_size, newline_size, size)) { |
| + /* value was malformed! */ |
| + return 1; |
| + } |
| + |
| + /* each element gets a newline too. */ |
| + *size += newline_size; |
| + } |
| + |
| + /* since we wrote out some elements, need to add a newline and indentation. |
| + */ |
| + /* to the trailing ']'. */ |
| + *size += depth * indent_size; |
| + } |
| + |
| + *size += 1; /* ']'. */ |
| + |
| + return 0; |
| +} |
| + |
| +json_weak int |
| +json_write_pretty_get_object_size(const struct json_object_s *object, |
| + size_t depth, size_t indent_size, |
| + size_t newline_size, size_t *size); |
| +int json_write_pretty_get_object_size(const struct json_object_s *object, |
| + size_t depth, size_t indent_size, |
| + size_t newline_size, size_t *size) { |
| + struct json_object_element_s *element; |
| + |
| + *size += 1; /* '{'. */ |
| + |
| + if (0 < object->length) { |
| + *size += newline_size; /* need a newline next. */ |
| + |
| + *size += object->length - 1; /* ','s seperate each element. */ |
| + |
| + for (element = object->start; json_null != element; |
| + element = element->next) { |
| + /* each element gets an indent and newline. */ |
| + *size += (depth + 1) * indent_size; |
| + *size += newline_size; |
| + |
| + if (json_write_get_string_size(element->name, size)) { |
| + /* string was malformed! */ |
| + return 1; |
| + } |
| + |
| + *size += 3; /* seperate each name/value pair with " : ". */ |
| + |
| + if (json_write_pretty_get_value_size(element->value, depth + 1, |
| + indent_size, newline_size, size)) { |
| + /* value was malformed! */ |
| + return 1; |
| + } |
| + } |
| + |
| + *size += depth * indent_size; |
| + } |
| + |
| + *size += 1; /* '}'. */ |
| + |
| + return 0; |
| +} |
| + |
| +json_weak int json_write_pretty_get_value_size(const struct json_value_s *value, |
| + size_t depth, size_t indent_size, |
| + size_t newline_size, |
| + size_t *size); |
| +int json_write_pretty_get_value_size(const struct json_value_s *value, |
| + size_t depth, size_t indent_size, |
| + size_t newline_size, size_t *size) { |
| + switch (value->type) { |
| + default: |
| + /* unknown value type found! */ |
| + return 1; |
| + case json_type_number: |
| + return json_write_get_number_size((struct json_number_s *)value->payload, |
| + size); |
| + case json_type_string: |
| + return json_write_get_string_size((struct json_string_s *)value->payload, |
| + size); |
| + case json_type_array: |
| + return json_write_pretty_get_array_size( |
| + (struct json_array_s *)value->payload, depth, indent_size, newline_size, |
| + size); |
| + case json_type_object: |
| + return json_write_pretty_get_object_size( |
| + (struct json_object_s *)value->payload, depth, indent_size, |
| + newline_size, size); |
| + case json_type_true: |
| + *size += 4; /* the string "true". */ |
| + return 0; |
| + case json_type_false: |
| + *size += 5; /* the string "false". */ |
| + return 0; |
| + case json_type_null: |
| + *size += 4; /* the string "null". */ |
| + return 0; |
| + } |
| +} |
| + |
| +json_weak char *json_write_pretty_value(const struct json_value_s *value, |
| + size_t depth, const char *indent, |
| + const char *newline, char *data); |
| + |
| +json_weak char *json_write_pretty_array(const struct json_array_s *array, |
| + size_t depth, const char *indent, |
| + const char *newline, char *data); |
| +char *json_write_pretty_array(const struct json_array_s *array, size_t depth, |
| + const char *indent, const char *newline, |
| + char *data) { |
| + size_t k, m; |
| + struct json_array_element_s *element; |
| + |
| + *data++ = '['; /* open the array. */ |
| + |
| + if (0 < array->length) { |
| + for (k = 0; '\0' != newline[k]; k++) { |
| + *data++ = newline[k]; |
| + } |
| + |
| + for (element = array->start; json_null != element; |
| + element = element->next) { |
| + if (element != array->start) { |
| + *data++ = ','; /* ','s seperate each element. */ |
| + |
| + for (k = 0; '\0' != newline[k]; k++) { |
| + *data++ = newline[k]; |
| + } |
| + } |
| + |
| + for (k = 0; k < depth + 1; k++) { |
| + for (m = 0; '\0' != indent[m]; m++) { |
| + *data++ = indent[m]; |
| + } |
| + } |
| + |
| + data = json_write_pretty_value(element->value, depth + 1, indent, newline, |
| + data); |
| + |
| + if (json_null == data) { |
| + /* value was malformed! */ |
| + return json_null; |
| + } |
| + } |
| + |
| + for (k = 0; '\0' != newline[k]; k++) { |
| + *data++ = newline[k]; |
| + } |
| + |
| + for (k = 0; k < depth; k++) { |
| + for (m = 0; '\0' != indent[m]; m++) { |
| + *data++ = indent[m]; |
| + } |
| + } |
| + } |
| + |
| + *data++ = ']'; /* close the array. */ |
| + |
| + return data; |
| +} |
| + |
| +json_weak char *json_write_pretty_object(const struct json_object_s *object, |
| + size_t depth, const char *indent, |
| + const char *newline, char *data); |
| +char *json_write_pretty_object(const struct json_object_s *object, size_t depth, |
| + const char *indent, const char *newline, |
| + char *data) { |
| + size_t k, m; |
| + struct json_object_element_s *element; |
| + |
| + *data++ = '{'; /* open the object. */ |
| + |
| + if (0 < object->length) { |
| + for (k = 0; '\0' != newline[k]; k++) { |
| + *data++ = newline[k]; |
| + } |
| + |
| + for (element = object->start; json_null != element; |
| + element = element->next) { |
| + if (element != object->start) { |
| + *data++ = ','; /* ','s seperate each element. */ |
| + |
| + for (k = 0; '\0' != newline[k]; k++) { |
| + *data++ = newline[k]; |
| + } |
| + } |
| + |
| + for (k = 0; k < depth + 1; k++) { |
| + for (m = 0; '\0' != indent[m]; m++) { |
| + *data++ = indent[m]; |
| + } |
| + } |
| + |
| + data = json_write_string(element->name, data); |
| + |
| + if (json_null == data) { |
| + /* string was malformed! */ |
| + return json_null; |
| + } |
| + |
| + /* " : "s seperate each name/value pair. */ |
| + *data++ = ' '; |
| + *data++ = ':'; |
| + *data++ = ' '; |
| + |
| + data = json_write_pretty_value(element->value, depth + 1, indent, newline, |
| + data); |
| + |
| + if (json_null == data) { |
| + /* value was malformed! */ |
| + return json_null; |
| + } |
| + } |
| + |
| + for (k = 0; '\0' != newline[k]; k++) { |
| + *data++ = newline[k]; |
| + } |
| + |
| + for (k = 0; k < depth; k++) { |
| + for (m = 0; '\0' != indent[m]; m++) { |
| + *data++ = indent[m]; |
| + } |
| + } |
| + } |
| + |
| + *data++ = '}'; /* close the object. */ |
| + |
| + return data; |
| +} |
| + |
| +json_weak char *json_write_pretty_value(const struct json_value_s *value, |
| + size_t depth, const char *indent, |
| + const char *newline, char *data); |
| +char *json_write_pretty_value(const struct json_value_s *value, size_t depth, |
| + const char *indent, const char *newline, |
| + char *data) { |
| + switch (value->type) { |
| + default: |
| + /* unknown value type found! */ |
| + return json_null; |
| + case json_type_number: |
| + return json_write_number((struct json_number_s *)value->payload, data); |
| + case json_type_string: |
| + return json_write_string((struct json_string_s *)value->payload, data); |
| + case json_type_array: |
| + return json_write_pretty_array((struct json_array_s *)value->payload, depth, |
| + indent, newline, data); |
| + case json_type_object: |
| + return json_write_pretty_object((struct json_object_s *)value->payload, |
| + depth, indent, newline, data); |
| + case json_type_true: |
| + data[0] = 't'; |
| + data[1] = 'r'; |
| + data[2] = 'u'; |
| + data[3] = 'e'; |
| + return data + 4; |
| + case json_type_false: |
| + data[0] = 'f'; |
| + data[1] = 'a'; |
| + data[2] = 'l'; |
| + data[3] = 's'; |
| + data[4] = 'e'; |
| + return data + 5; |
| + case json_type_null: |
| + data[0] = 'n'; |
| + data[1] = 'u'; |
| + data[2] = 'l'; |
| + data[3] = 'l'; |
| + return data + 4; |
| + } |
| +} |
| + |
| +void *json_write_pretty(const struct json_value_s *value, const char *indent, |
| + const char *newline, size_t *out_size) { |
| + size_t size = 0; |
| + size_t indent_size = 0; |
| + size_t newline_size = 0; |
| + char *data = json_null; |
| + char *data_end = json_null; |
| + |
| + if (json_null == value) { |
| + return json_null; |
| + } |
| + |
| + if (json_null == indent) { |
| + indent = " "; /* default to two spaces. */ |
| + } |
| + |
| + if (json_null == newline) { |
| + newline = "\n"; /* default to linux newlines. */ |
| + } |
| + |
| + while ('\0' != indent[indent_size]) { |
| + ++indent_size; /* skip non-null terminating characters. */ |
| + } |
| + |
| + while ('\0' != newline[newline_size]) { |
| + ++newline_size; /* skip non-null terminating characters. */ |
| + } |
| + |
| + if (json_write_pretty_get_value_size(value, 0, indent_size, newline_size, |
| + &size)) { |
| + /* value was malformed! */ |
| + return json_null; |
| + } |
| + |
| + size += 1; /* for the '\0' null terminating character. */ |
| + |
| + data = (char *)malloc(size); |
| + |
| + if (json_null == data) { |
| + /* malloc failed! */ |
| + return json_null; |
| + } |
| + |
| + data_end = json_write_pretty_value(value, 0, indent, newline, data); |
| + |
| + if (json_null == data_end) { |
| + /* bad chi occurred! */ |
| + free(data); |
| + return json_null; |
| + } |
| + |
| + /* null terminated the string. */ |
| + *data_end = '\0'; |
| + |
| + if (json_null != out_size) { |
| + *out_size = size; |
| + } |
| + |
| + return data; |
| +} |
| + |
| +#if defined(__clang__) |
| +#pragma clang diagnostic pop |
| +#elif defined(_MSC_VER) |
| +#pragma warning(pop) |
| +#endif |
| + |
| +#endif /* SHEREDOM_JSON_H_INCLUDED. */ |
| diff --git a/src/swtpm_setup/swtpm_setup.c b/src/swtpm_setup/swtpm_setup.c |
| index 96e8b9f..ade5e34 100644 |
| --- a/src/swtpm_setup/swtpm_setup.c |
| +++ b/src/swtpm_setup/swtpm_setup.c |
| @@ -5,6 +5,7 @@ |
| * Author: Stefan Berger, stefanb@linux.ibm.com |
| * |
| * Copyright (c) IBM Corporation, 2021 |
| + * Copyright (c) Monogon SE, 2024 |
| */ |
| |
| #include "config.h" |
| @@ -27,8 +28,7 @@ |
| #include "glib/gstdio.h" |
| #include "glib/gprintf.h" |
| |
| -#include <glib-object.h> |
| -#include <json-glib/json-glib.h> |
| +#include "json.h" |
| |
| #include <libtpms/tpm_nvfilename.h> |
| |
| @@ -99,78 +99,112 @@ static int tpm_get_specs_and_attributes(struct swtpm *swtpm, gchar ***params) |
| { |
| int ret; |
| g_autofree gchar *json = NULL; |
| - JsonParser *jp = NULL; |
| - GError *error = NULL; |
| - JsonReader *jr = NULL; |
| - JsonNode *root; |
| - static const struct parse_rule { |
| - const char *node1; |
| - const char *node2; |
| - gboolean is_int; |
| - const char *optname; |
| - } parser_rules[7] = { |
| - {"TPMSpecification", "family", FALSE, "--tpm-spec-family"}, |
| - {"TPMSpecification", "level", TRUE, "--tpm-spec-level"}, |
| - {"TPMSpecification", "revision", TRUE, "--tpm-spec-revision"}, |
| - {"TPMAttributes", "manufacturer", FALSE, "--tpm-manufacturer"}, |
| - {"TPMAttributes", "model", FALSE, "--tpm-model"}, |
| - {"TPMAttributes", "version", FALSE, "--tpm-version"}, |
| - {NULL, NULL, FALSE, NULL}, |
| - }; |
| - size_t idx; |
| + struct json_value_s *root = NULL; |
| + struct json_object_s *obj = NULL; |
| + |
| + *params = NULL; |
| |
| ret = swtpm->cops->ctrl_get_tpm_specs_and_attrs(swtpm, &json); |
| if (ret != 0) { |
| logerr(gl_LOGFILE, "Could not get the TPM spec and attribute parameters.\n"); |
| - return 1; |
| + ret = 1; |
| + goto error; |
| } |
| |
| - jp = json_parser_new(); |
| + root = json_parse(json, strlen(json)); |
| + if (root == 0) { |
| + logerr(gl_LOGFILE, "Could not parse JSON from TPM spec and attribute parameters.\n"); |
| + ret = 1; |
| + goto error; |
| + } |
| + obj = json_value_as_object(root); |
| + if (obj == NULL) { |
| + logerr(gl_LOGFILE, "JSON root is not an object.\n"); |
| + ret = 1; |
| + goto error; |
| + } |
| |
| - if (!json_parser_load_from_data(jp, json, -1, &error)) { |
| - logerr(gl_LOGFILE, "JSON parser failed: %s\n", error->message); |
| - g_error_free(error); |
| + struct json_object_s *TPMSpecification = NULL; |
| + struct json_object_s *TPMAttributes = NULL; |
| + for (struct json_object_element_s *el = obj->start; el != NULL; el = el->next) { |
| + if (strcmp(el->name->string, "TPMSpecification") == 0) { |
| + TPMSpecification = json_value_as_object(el->value); |
| + continue; |
| + } |
| + if (strcmp(el->name->string, "TPMAttributes") == 0) { |
| + TPMAttributes = json_value_as_object(el->value); |
| + continue; |
| + } |
| + } |
| + if (TPMSpecification == NULL) { |
| + logerr(gl_LOGFILE, "TPMSpecification not found in JSON.\n"); |
| + ret = 1; |
| + goto error; |
| + } |
| + if (TPMAttributes == NULL) { |
| + logerr(gl_LOGFILE, "TPMAttributes not found in JSON.\n"); |
| + ret = 1; |
| goto error; |
| } |
| |
| - *params = NULL; |
| - root = json_parser_get_root(jp); |
| - |
| - for (idx = 0; parser_rules[idx].node1 != NULL; idx++) { |
| - jr = json_reader_new(root); |
| - if (json_reader_read_member(jr, parser_rules[idx].node1) && |
| - json_reader_read_member(jr, parser_rules[idx].node2)) { |
| - gchar *str; |
| - |
| - if (parser_rules[idx].is_int) |
| - str = g_strdup_printf("%ld", (long)json_reader_get_int_value(jr)); |
| - else |
| - str = g_strdup(json_reader_get_string_value(jr)); |
| - |
| - *params = concat_arrays(*params, |
| - (gchar*[]){ |
| - g_strdup(parser_rules[idx].optname), |
| - str, |
| - NULL |
| - }, TRUE); |
| - } else { |
| - logerr(gl_LOGFILE, "Could not find [%s][%s] in '%s'\n", |
| - parser_rules[idx].node1, parser_rules[idx].node2, json); |
| - ret = 1; |
| - break; |
| + const char *spec_family = NULL; |
| + const char *spec_level = NULL; |
| + const char *spec_revision = NULL; |
| + const char *attr_manufacturer = NULL; |
| + const char *attr_model = NULL; |
| + const char *attr_version = NULL; |
| + |
| + for (struct json_object_element_s *el = TPMSpecification->start; el != NULL; el = el->next) { |
| + if (strcmp(el->name->string, "family") == 0 && el->value->type == json_type_string) { |
| + spec_family = json_value_as_string(el->value)->string; |
| + continue; |
| + } |
| + if (strcmp(el->name->string, "level") == 0 && el->value->type == json_type_number) { |
| + spec_level = json_value_as_number(el->value)->number; |
| + continue; |
| + } |
| + if (strcmp(el->name->string, "revision") == 0 && el->value->type == json_type_number) { |
| + spec_revision = json_value_as_number(el->value)->number; |
| + continue; |
| + } |
| + } |
| + for (struct json_object_element_s *el = TPMAttributes->start; el != NULL; el = el->next) { |
| + if (strcmp(el->name->string, "manufacturer") == 0 && el->value->type == json_type_string) { |
| + attr_manufacturer = json_value_as_string(el->value)->string; |
| + continue; |
| + } |
| + if (strcmp(el->name->string, "model") == 0 && el->value->type == json_type_string) { |
| + attr_model = json_value_as_string(el->value)->string; |
| + continue; |
| + } |
| + if (strcmp(el->name->string, "version") == 0 && el->value->type == json_type_string) { |
| + attr_version = json_value_as_string(el->value)->string; |
| + continue; |
| } |
| - g_object_unref(jr); |
| - jr = NULL; |
| } |
| |
| + if (!spec_family) { logerr(gl_LOGFILE, "family not found in TPMSpecifiction.\n"); ret = 1; goto error; } |
| + if (!spec_level) { logerr(gl_LOGFILE, "level not found in TPMSpecifiction.\n"); ret = 1; goto error; } |
| + if (!spec_revision) { logerr(gl_LOGFILE, "revision not found in TPMSpecifiction.\n"); ret = 1; goto error; } |
| + if (!attr_manufacturer) { logerr(gl_LOGFILE, "manufacturer not found in TPMAttributes.\n"); ret = 1; goto error; } |
| + if (!attr_model) { logerr(gl_LOGFILE, "model not found in TPMAttributes.\n"); ret = 1; goto error; } |
| + if (!attr_version) { logerr(gl_LOGFILE, "version not found in TPMAttributes.\n"); ret = 1; goto error; } |
| + |
| + *params = concat_arrays(*params, (gchar*[]) { |
| + g_strdup("--tpm-spec-family"), g_strdup(spec_family), |
| + g_strdup("--tpm-spec-revision"), g_strdup(spec_revision), |
| + g_strdup("--tpm-spec-level"), g_strdup(spec_level), |
| + g_strdup("--tpm-manufacturer"), g_strdup(attr_manufacturer), |
| + g_strdup("--tpm-model"), g_strdup(attr_model), |
| + g_strdup("--tpm-version"), g_strdup(attr_version), |
| + NULL }, TRUE); |
| +error: |
| if (ret) { |
| g_strfreev(*params); |
| - *params = NULL; |
| - g_object_unref(jr); |
| } |
| -error: |
| - g_object_unref(jp); |
| - |
| + if (root) { |
| + free(root); |
| + } |
| return ret; |
| } |
| |
| -- |
| 2.42.0 |
| |