Sfoglia il codice sorgente

Merge branch 'develop' (Release 1.4.0)

Max Bruckner 8 anni fa
parent
commit
030d0c14cc

+ 54 - 0
.github/CONTRIBUTING.md

@@ -0,0 +1,54 @@
+Contribution Guidelines
+=======================
+
+Contributions to cJSON are welcome. If you find a bug or want to improve cJSON in another way, pull requests are appreciated.
+
+For bigger changes, in order to avoid wasted effort, please open an issue to discuss the technical details before creating a pull request.
+
+The further sections explain the process in more detail and provides some guidelines on how contributions should look like.
+
+Branches
+--------
+There are two branches to be aware of, the `master` and the `develop` branch. The `master` branch is reserved for the latest release, so only make pull requests to the `master` branch for small bug- or security fixes (these are usually just a few lines). In all other cases, please make a pull request to the `develop` branch.
+
+Coding Style
+------------
+The coding style has been discussed in [#24](https://github.com/DaveGamble/cJSON/issues/24). The basics are:
+
+* Use 4 tabs for indentation
+* No oneliners (conditions, loops, variable declarations ...)
+* Always use parenthesis for control structures
+* Don't implicitly rely on operator precedence, use round brackets in expressions. e.g. `(a > b) && (c < d)` instead of `a>b && c<d`
+* opening curly braces start in the next line
+* use spaces around operators
+* lines should not have trailing whitespace
+* use spaces between function parameters
+* use pronouncable variable names, not just a combination of letters
+
+Example:
+
+```c
+/* calculate the new length of the string in a printbuffer and update the offset */
+static void update_offset(printbuffer * const buffer)
+{
+    const unsigned char *buffer_pointer = NULL;
+    if ((buffer == NULL) || (buffer->buffer == NULL))
+    {
+        return;
+    }
+    buffer_pointer = buffer->buffer + buffer->offset;
+
+    buffer->offset += strlen((const char*)buffer_pointer);
+}
+```
+
+Unit Tests
+----------
+cJSON uses the [Unity](https://github.com/ThrowTheSwitch/Unity) library for unit tests. The tests are located in the `tests` directory. In order to add a new test, either add it to one of the existing files (if it fits) or add a new C file for the test. That new file has to be added to the list of tests in `tests/CMakeLists.txt`.
+
+All new features have to be covered by unit tests.
+
+Other Notes
+-----------
+* Internal functions are to be declared static.
+* Wrap the return type of external function in the `CJSON_PUBLIC` macro.

+ 16 - 3
CMakeLists.txt

@@ -1,15 +1,15 @@
 set(CMAKE_LEGACY_CYGWIN_WIN32 0)
 set(CMAKE_LEGACY_CYGWIN_WIN32 0)
 cmake_minimum_required(VERSION 2.8)
 cmake_minimum_required(VERSION 2.8)
 
 
-subdirs(tests)
+subdirs(tests fuzzing)
 
 
 include(GNUInstallDirs)
 include(GNUInstallDirs)
 
 
 project(cJSON C)
 project(cJSON C)
 
 
 set(PROJECT_VERSION_MAJOR 1)
 set(PROJECT_VERSION_MAJOR 1)
-set(PROJECT_VERSION_MINOR 3)
-set(PROJECT_VERSION_PATCH 2)
+set(PROJECT_VERSION_MINOR 4)
+set(PROJECT_VERSION_PATCH 0)
 set(CJSON_VERSION_SO 1)
 set(CJSON_VERSION_SO 1)
 set(CJSON_UTILS_VERSION_SO 1)
 set(CJSON_UTILS_VERSION_SO 1)
 set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
 set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
@@ -40,6 +40,9 @@ if (ENABLE_CUSTOM_COMPILER_FLAGS)
         -Wconversion
         -Wconversion
         -Wc++-compat
         -Wc++-compat
         -fstack-protector-strong
         -fstack-protector-strong
+        -Wcomma
+        -Wdouble-promotion
+        -Wparentheses
         )
         )
 endif()
 endif()
 
 
@@ -58,6 +61,16 @@ if (ENABLE_SANITIZERS)
         )
         )
 endif()
 endif()
 
 
+option(ENABLE_PUBLIC_SYMBOLS "Export library symbols." On)
+if (ENABLE_PUBLIC_SYMBOLS)
+    list(APPEND custom_compiler_flags -fvisibility=hidden)
+    add_definitions(-DCJSON_EXPORT_SYMBOLS -DCJSON_API_VISIBILITY)
+endif()
+option(ENABLE_HIDDEN_SYMBOLS "Hide library symbols." Off)
+if (ENABLE_HIDDEN_SYMBOLS)
+    add_definitions(-DCJSON_HIDE_SYMBOLS -UCJSON_API_VISIBILITY)
+endif()
+
 # apply custom compiler flags
 # apply custom compiler flags
 foreach(compiler_flag ${custom_compiler_flags})
 foreach(compiler_flag ${custom_compiler_flags})
     #remove problematic characters
     #remove problematic characters

+ 1 - 0
CONTRIBUTORS.md

@@ -21,6 +21,7 @@ Contributors
 * [Linus Wallgren](https://github.com/ecksun)
 * [Linus Wallgren](https://github.com/ecksun)
 * [Max Bruckner](https://github.com/FSMaxB)
 * [Max Bruckner](https://github.com/FSMaxB)
 * Mike Pontillo
 * Mike Pontillo
+* [Mike Jerris](https://github.com/mjerris)
 * Paulo Antonio Alvarez
 * Paulo Antonio Alvarez
 * [Rafael Leal Dias](https://github.com/rafaeldias)
 * [Rafael Leal Dias](https://github.com/rafaeldias)
 * [Rod Vagg](https://github.com/rvagg)
 * [Rod Vagg](https://github.com/rvagg)

+ 1 - 1
Makefile

@@ -10,7 +10,7 @@ UTILS_TEST_SRC = cJSON.c cJSON_Utils.c test_utils.c
 
 
 LDLIBS = -lm
 LDLIBS = -lm
 
 
-LIBVERSION = 1.3.2
+LIBVERSION = 1.4.0
 CJSON_SOVERSION = 1
 CJSON_SOVERSION = 1
 UTILS_SOVERSION = 1
 UTILS_SOVERSION = 1
 
 

File diff suppressed because it is too large
+ 374 - 348
cJSON.c


+ 100 - 48
cJSON.h

@@ -30,11 +30,8 @@ extern "C"
 
 
 /* project version */
 /* project version */
 #define CJSON_VERSION_MAJOR 1
 #define CJSON_VERSION_MAJOR 1
-#define CJSON_VERSION_MINOR 3
-#define CJSON_VERSION_PATCH 2
-
-/* returns the version of cJSON as a string */
-extern const char* cJSON_Version(void);
+#define CJSON_VERSION_MINOR 4
+#define CJSON_VERSION_PATCH 0
 
 
 #include <stddef.h>
 #include <stddef.h>
 
 
@@ -81,84 +78,139 @@ typedef struct cJSON_Hooks
       void (*free_fn)(void *ptr);
       void (*free_fn)(void *ptr);
 } cJSON_Hooks;
 } cJSON_Hooks;
 
 
+typedef int cJSON_bool;
+
+#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
+#define __WINDOWS__
+#endif
+#ifdef __WINDOWS__
+
+/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention.  For windows you have 2 define options:
+
+CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
+CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols
+
+For *nix builds that support visibility attribute, you can define similar behavior by 
+
+setting default visibility to hidden by adding
+-fvisibility=hidden (for gcc)
+or
+-xldscope=hidden (for sun cc)
+to CFLAGS
+
+then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
+
+*/
+
+#if defined(CJSON_HIDE_SYMBOLS)
+#define CJSON_PUBLIC(type)   type __stdcall
+#elif defined(CJSON_EXPORT_SYMBOLS)
+#define CJSON_PUBLIC(type)   __declspec(dllexport) type __stdcall
+#else
+#define CJSON_PUBLIC(type)   __declspec(dllimport) type __stdcall
+#endif
+#else /* !WIN32 */
+#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
+#define CJSON_PUBLIC(type)   __attribute__((visibility("default"))) type
+#else
+#define CJSON_PUBLIC(type) type
+#endif
+#endif
+
+/* returns the version of cJSON as a string */
+CJSON_PUBLIC(const char*) cJSON_Version(void);
+
 /* Supply malloc, realloc and free functions to cJSON */
 /* Supply malloc, realloc and free functions to cJSON */
-extern void cJSON_InitHooks(cJSON_Hooks* hooks);
+CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
 
 
 
 
 /* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */
 /* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */
-extern cJSON *cJSON_Parse(const char *value);
+CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
 /* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */
 /* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */
-extern char  *cJSON_Print(const cJSON *item);
+CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
 /* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */
 /* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */
-extern char  *cJSON_PrintUnformatted(const cJSON *item);
+CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
 /* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
 /* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
-extern char *cJSON_PrintBuffered(const cJSON *item, int prebuffer, int fmt);
+CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
 /* Render a cJSON entity to text using a buffer already allocated in memory with length buf_len. Returns 1 on success and 0 on failure. */
 /* Render a cJSON entity to text using a buffer already allocated in memory with length buf_len. Returns 1 on success and 0 on failure. */
-extern int cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const int fmt);
+CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const cJSON_bool fmt);
 /* Delete a cJSON entity and all subentities. */
 /* Delete a cJSON entity and all subentities. */
-extern void   cJSON_Delete(cJSON *c);
+CJSON_PUBLIC(void) cJSON_Delete(cJSON *c);
 
 
 /* Returns the number of items in an array (or object). */
 /* Returns the number of items in an array (or object). */
-extern int	  cJSON_GetArraySize(const cJSON *array);
+CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
 /* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
 /* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */
-extern cJSON *cJSON_GetArrayItem(const cJSON *array, int item);
+CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int item);
 /* Get item "string" from object. Case insensitive. */
 /* Get item "string" from object. Case insensitive. */
-extern cJSON *cJSON_GetObjectItem(const cJSON *object, const char *string);
-extern int cJSON_HasObjectItem(const cJSON *object, const char *string);
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON *object, const char *string);
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON *object, const char *string);
+CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
 /* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
 /* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
-extern const char *cJSON_GetErrorPtr(void);
+CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
+
+/* These functions check the type of an item */
+CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
 
 
 /* These calls create a cJSON item of the appropriate type. */
 /* These calls create a cJSON item of the appropriate type. */
-extern cJSON *cJSON_CreateNull(void);
-extern cJSON *cJSON_CreateTrue(void);
-extern cJSON *cJSON_CreateFalse(void);
-extern cJSON *cJSON_CreateBool(int b);
-extern cJSON *cJSON_CreateNumber(double num);
-extern cJSON *cJSON_CreateString(const char *string);
+CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
+CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
+CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
 /* raw json */
 /* raw json */
-extern cJSON *cJSON_CreateRaw(const char *raw);
-extern cJSON *cJSON_CreateArray(void);
-extern cJSON *cJSON_CreateObject(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
+CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
 
 
 /* These utilities create an Array of count items. */
 /* These utilities create an Array of count items. */
-extern cJSON *cJSON_CreateIntArray(const int *numbers, int count);
-extern cJSON *cJSON_CreateFloatArray(const float *numbers, int count);
-extern cJSON *cJSON_CreateDoubleArray(const double *numbers, int count);
-extern cJSON *cJSON_CreateStringArray(const char **strings, int count);
+CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
+CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
+CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count);
 
 
 /* Append item to the specified array/object. */
 /* Append item to the specified array/object. */
-extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
-extern void	cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
+CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item);
+CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
 /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
 /* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
  * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
  * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
  * writing to `item->string` */
  * writing to `item->string` */
-extern void	cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
+CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
 /* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
 /* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
-extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
-extern void	cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
+CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
+CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
 
 
 /* Remove/Detatch items from Arrays/Objects. */
 /* Remove/Detatch items from Arrays/Objects. */
-extern cJSON *cJSON_DetachItemFromArray(cJSON *array, int which);
-extern void   cJSON_DeleteItemFromArray(cJSON *array, int which);
-extern cJSON *cJSON_DetachItemFromObject(cJSON *object, const char *string);
-extern void   cJSON_DeleteItemFromObject(cJSON *object, const char *string);
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
+CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
 
 
 /* Update array items. */
 /* Update array items. */
-extern void cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
-extern void cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
-extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
+CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
+CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
+CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
 
 
 /* Duplicate a cJSON item */
 /* Duplicate a cJSON item */
-extern cJSON *cJSON_Duplicate(const cJSON *item, int recurse);
+CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
 /* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
 /* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
 need to be released. With recurse!=0, it will duplicate any children connected to the item.
 need to be released. With recurse!=0, it will duplicate any children connected to the item.
 The item->next and ->prev pointers are always zero on return from Duplicate. */
 The item->next and ->prev pointers are always zero on return from Duplicate. */
 
 
 /* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
 /* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
 /* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error. If not, then cJSON_GetErrorPtr() does the job. */
 /* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error. If not, then cJSON_GetErrorPtr() does the job. */
-extern cJSON *cJSON_ParseWithOpts(const char *value, const char **return_parse_end, int require_null_terminated);
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
 
 
-extern void cJSON_Minify(char *json);
+CJSON_PUBLIC(void) cJSON_Minify(char *json);
 
 
 /* Macros for creating things quickly. */
 /* Macros for creating things quickly. */
 #define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull())
 #define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull())
@@ -172,11 +224,11 @@ extern void cJSON_Minify(char *json);
 /* When assigning an integer value, it needs to be propagated to valuedouble too. */
 /* When assigning an integer value, it needs to be propagated to valuedouble too. */
 #define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
 #define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
 /* helper for the cJSON_SetNumberValue macro */
 /* helper for the cJSON_SetNumberValue macro */
-extern double cJSON_SetNumberHelper(cJSON *object, double number);
-#define cJSON_SetNumberValue(object, number) ((object) ? cJSON_SetNumberHelper(object, (double)number) : (number))
+CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
+#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
 
 
 /* Macro for iterating over an array */
 /* Macro for iterating over an array */
-#define cJSON_ArrayForEach(pos, head) for(pos = (head)->child; pos != NULL; pos = pos->next)
+#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
 }
 }

+ 32 - 31
cJSON_Utils.c

@@ -1,8 +1,10 @@
+#pragma GCC visibility push(default)
 #include <ctype.h>
 #include <ctype.h>
 #include <string.h>
 #include <string.h>
 #include <stdlib.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <stdio.h>
 #include <limits.h>
 #include <limits.h>
+#pragma GCC visibility pop
 
 
 #include "cJSON_Utils.h"
 #include "cJSON_Utils.h"
 
 
@@ -31,7 +33,7 @@ static int cJSONUtils_strcasecmp(const unsigned char *s1, const unsigned char *s
     {
     {
         return 1;
         return 1;
     }
     }
-    for(; tolower(*s1) == tolower(*s2); ++s1, ++s2)
+    for(; tolower(*s1) == tolower(*s2); (void)++s1, ++s2)
     {
     {
         if(*s1 == 0)
         if(*s1 == 0)
         {
         {
@@ -49,7 +51,7 @@ static int cJSONUtils_Pstrcasecmp(const unsigned char *a, const unsigned char *e
     {
     {
         return (a == e) ? 0 : 1; /* both NULL? */
         return (a == e) ? 0 : 1; /* both NULL? */
     }
     }
-    for (; *a && *e && (*e != '/'); a++, e++) /* compare until next '/' */
+    for (; *a && *e && (*e != '/'); (void)a++, e++) /* compare until next '/' */
     {
     {
         if (*e == '~')
         if (*e == '~')
         {
         {
@@ -81,7 +83,7 @@ static int cJSONUtils_Pstrcasecmp(const unsigned char *a, const unsigned char *e
 static size_t cJSONUtils_PointerEncodedstrlen(const unsigned char *s)
 static size_t cJSONUtils_PointerEncodedstrlen(const unsigned char *s)
 {
 {
     size_t l = 0;
     size_t l = 0;
-    for (; *s; s++, l++)
+    for (; *s; (void)s++, l++)
     {
     {
         if ((*s == '~') || (*s == '/'))
         if ((*s == '~') || (*s == '/'))
         {
         {
@@ -115,9 +117,8 @@ static void cJSONUtils_PointerEncodedstrcpy(unsigned char *d, const unsigned cha
     *d = '\0';
     *d = '\0';
 }
 }
 
 
-char *cJSONUtils_FindPointerFromObjectTo(cJSON *object, cJSON *target)
+CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(cJSON *object, cJSON *target)
 {
 {
-    int type = object->type;
     size_t c = 0;
     size_t c = 0;
     cJSON *obj = 0;
     cJSON *obj = 0;
 
 
@@ -128,12 +129,12 @@ char *cJSONUtils_FindPointerFromObjectTo(cJSON *object, cJSON *target)
     }
     }
 
 
     /* recursively search all children of the object */
     /* recursively search all children of the object */
-    for (obj = object->child; obj; obj = obj->next, c++)
+    for (obj = object->child; obj; (void)(obj = obj->next), c++)
     {
     {
         unsigned char *found = (unsigned char*)cJSONUtils_FindPointerFromObjectTo(obj, target);
         unsigned char *found = (unsigned char*)cJSONUtils_FindPointerFromObjectTo(obj, target);
         if (found)
         if (found)
         {
         {
-            if ((type & 0xFF) == cJSON_Array)
+            if (cJSON_IsArray(object))
             {
             {
                 /* reserve enough memory for a 64 bit integer + '/' and '\0' */
                 /* reserve enough memory for a 64 bit integer + '/' and '\0' */
                 unsigned char *ret = (unsigned char*)malloc(strlen((char*)found) + 23);
                 unsigned char *ret = (unsigned char*)malloc(strlen((char*)found) + 23);
@@ -150,7 +151,7 @@ char *cJSONUtils_FindPointerFromObjectTo(cJSON *object, cJSON *target)
 
 
                 return (char*)ret;
                 return (char*)ret;
             }
             }
-            else if ((type & 0xFF) == cJSON_Object)
+            else if (cJSON_IsObject(object))
             {
             {
                 unsigned char *ret = (unsigned char*)malloc(strlen((char*)found) + cJSONUtils_PointerEncodedstrlen((unsigned char*)obj->string) + 2);
                 unsigned char *ret = (unsigned char*)malloc(strlen((char*)found) + cJSONUtils_PointerEncodedstrlen((unsigned char*)obj->string) + 2);
                 *ret = '/';
                 *ret = '/';
@@ -171,12 +172,12 @@ char *cJSONUtils_FindPointerFromObjectTo(cJSON *object, cJSON *target)
     return NULL;
     return NULL;
 }
 }
 
 
-cJSON *cJSONUtils_GetPointer(cJSON *object, const char *pointer)
+CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointer(cJSON *object, const char *pointer)
 {
 {
     /* follow path of the pointer */
     /* follow path of the pointer */
     while ((*pointer++ == '/') && object)
     while ((*pointer++ == '/') && object)
     {
     {
-        if ((object->type & 0xFF) == cJSON_Array)
+        if (cJSON_IsArray(object))
         {
         {
             size_t which = 0;
             size_t which = 0;
             /* parse array index */
             /* parse array index */
@@ -195,7 +196,7 @@ cJSON *cJSONUtils_GetPointer(cJSON *object, const char *pointer)
             }
             }
             object = cJSON_GetArrayItem(object, (int)which);
             object = cJSON_GetArrayItem(object, (int)which);
         }
         }
-        else if ((object->type & 0xFF) == cJSON_Object)
+        else if (cJSON_IsObject(object))
         {
         {
             object = object->child;
             object = object->child;
             /* GetObjectItem. */
             /* GetObjectItem. */
@@ -227,7 +228,7 @@ static void cJSONUtils_InplaceDecodePointerString(unsigned char *string)
         return;
         return;
     }
     }
 
 
-    for (; *string; s2++, string++)
+    for (; *string; (void)s2++, string++)
     {
     {
         *s2 = (*string != '~')
         *s2 = (*string != '~')
             ? (*string)
             ? (*string)
@@ -269,11 +270,11 @@ static cJSON *cJSONUtils_PatchDetach(cJSON *object, const unsigned char *path)
         /* Couldn't find object to remove child from. */
         /* Couldn't find object to remove child from. */
         ret = NULL;
         ret = NULL;
     }
     }
-    else if ((parent->type & 0xFF) == cJSON_Array)
+    else if (cJSON_IsArray(parent))
     {
     {
         ret = cJSON_DetachItemFromArray(parent, atoi((char*)childptr));
         ret = cJSON_DetachItemFromArray(parent, atoi((char*)childptr));
     }
     }
-    else if ((parent->type & 0xFF) == cJSON_Object)
+    else if (cJSON_IsObject(parent))
     {
     {
         ret = cJSON_DetachItemFromObject(parent, (char*)childptr);
         ret = cJSON_DetachItemFromObject(parent, (char*)childptr);
     }
     }
@@ -299,7 +300,7 @@ static int cJSONUtils_Compare(cJSON *a, cJSON *b)
             /* string mismatch. */
             /* string mismatch. */
             return (strcmp(a->valuestring, b->valuestring) != 0) ? -3 : 0;
             return (strcmp(a->valuestring, b->valuestring) != 0) ? -3 : 0;
         case cJSON_Array:
         case cJSON_Array:
-            for (a = a->child, b = b->child; a && b; a = a->next, b = b->next)
+            for ((void)(a = a->child), b = b->child; a && b; (void)(a = a->next), b = b->next)
             {
             {
                 int err = cJSONUtils_Compare(a, b);
                 int err = cJSONUtils_Compare(a, b);
                 if (err)
                 if (err)
@@ -474,7 +475,7 @@ static int cJSONUtils_ApplyPatch(cJSON *object, cJSON *patch)
         cJSON_Delete(value);
         cJSON_Delete(value);
         return 9;
         return 9;
     }
     }
-    else if ((parent->type & 0xFF) == cJSON_Array)
+    else if (cJSON_IsArray(parent))
     {
     {
         if (!strcmp((char*)childptr, "-"))
         if (!strcmp((char*)childptr, "-"))
         {
         {
@@ -485,7 +486,7 @@ static int cJSONUtils_ApplyPatch(cJSON *object, cJSON *patch)
             cJSON_InsertItemInArray(parent, atoi((char*)childptr), value);
             cJSON_InsertItemInArray(parent, atoi((char*)childptr), value);
         }
         }
     }
     }
-    else if ((parent->type & 0xFF) == cJSON_Object)
+    else if (cJSON_IsObject(parent))
     {
     {
         cJSON_DeleteItemFromObject(parent, (char*)childptr);
         cJSON_DeleteItemFromObject(parent, (char*)childptr);
         cJSON_AddItemToObject(parent, (char*)childptr, value);
         cJSON_AddItemToObject(parent, (char*)childptr, value);
@@ -499,7 +500,7 @@ static int cJSONUtils_ApplyPatch(cJSON *object, cJSON *patch)
     return 0;
     return 0;
 }
 }
 
 
-int cJSONUtils_ApplyPatches(cJSON *object, cJSON *patches)
+CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON *object, cJSON *patches)
 {
 {
     int err = 0;
     int err = 0;
 
 
@@ -508,7 +509,7 @@ int cJSONUtils_ApplyPatches(cJSON *object, cJSON *patches)
         return 1;
         return 1;
     }
     }
 
 
-    if ((patches->type & 0xFF) != cJSON_Array)
+    if (cJSON_IsArray(patches))
     {
     {
         /* malformed patches. */
         /* malformed patches. */
         return 1;
         return 1;
@@ -551,7 +552,7 @@ static void cJSONUtils_GeneratePatch(cJSON *patches, const unsigned char *op, co
     cJSON_AddItemToArray(patches, patch);
     cJSON_AddItemToArray(patches, patch);
 }
 }
 
 
-void cJSONUtils_AddPatchToArray(cJSON *array, const char *op, const char *path, cJSON *val)
+CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON *array, const char *op, const char *path, cJSON *val)
 {
 {
     cJSONUtils_GeneratePatch(array, (const unsigned char*)op, (const unsigned char*)path, 0, val);
     cJSONUtils_GeneratePatch(array, (const unsigned char*)op, (const unsigned char*)path, 0, val);
 }
 }
@@ -590,7 +591,7 @@ static void cJSONUtils_CompareToPatch(cJSON *patches, const unsigned char *path,
             size_t c = 0;
             size_t c = 0;
             unsigned char *newpath = (unsigned char*)malloc(strlen((const char*)path) + 23); /* Allow space for 64bit int. */
             unsigned char *newpath = (unsigned char*)malloc(strlen((const char*)path) + 23); /* Allow space for 64bit int. */
             /* generate patches for all array elements that exist in "from" and "to" */
             /* generate patches for all array elements that exist in "from" and "to" */
-            for (c = 0, from = from->child, to = to->child; from && to; from = from->next, to = to->next, c++)
+            for ((void)(c = 0), (void)(from = from->child), to = to->child; from && to; (void)(from = from->next), (void)(to = to->next), c++)
             {
             {
                 /* check if conversion to unsigned long is valid
                 /* check if conversion to unsigned long is valid
                  * This should be eliminated at compile time by dead code elimination
                  * This should be eliminated at compile time by dead code elimination
@@ -604,7 +605,7 @@ static void cJSONUtils_CompareToPatch(cJSON *patches, const unsigned char *path,
                 cJSONUtils_CompareToPatch(patches, newpath, from, to);
                 cJSONUtils_CompareToPatch(patches, newpath, from, to);
             }
             }
             /* remove leftover elements from 'from' that are not in 'to' */
             /* remove leftover elements from 'from' that are not in 'to' */
-            for (; from; from = from->next, c++)
+            for (; from; (void)(from = from->next), c++)
             {
             {
                 /* check if conversion to unsigned long is valid
                 /* check if conversion to unsigned long is valid
                  * This should be eliminated at compile time by dead code elimination
                  * This should be eliminated at compile time by dead code elimination
@@ -618,7 +619,7 @@ static void cJSONUtils_CompareToPatch(cJSON *patches, const unsigned char *path,
                 cJSONUtils_GeneratePatch(patches, (const unsigned char*)"remove", path, newpath, 0);
                 cJSONUtils_GeneratePatch(patches, (const unsigned char*)"remove", path, newpath, 0);
             }
             }
             /* add new elements in 'to' that were not in 'from' */
             /* add new elements in 'to' that were not in 'from' */
-            for (; to; to = to->next, c++)
+            for (; to; (void)(to = to->next), c++)
             {
             {
                 cJSONUtils_GeneratePatch(patches, (const unsigned char*)"add", path, (const unsigned char*)"-", to);
                 cJSONUtils_GeneratePatch(patches, (const unsigned char*)"add", path, (const unsigned char*)"-", to);
             }
             }
@@ -671,7 +672,7 @@ static void cJSONUtils_CompareToPatch(cJSON *patches, const unsigned char *path,
     }
     }
 }
 }
 
 
-cJSON* cJSONUtils_GeneratePatches(cJSON *from, cJSON *to)
+CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON *from, cJSON *to)
 {
 {
     cJSON *patches = cJSON_CreateArray();
     cJSON *patches = cJSON_CreateArray();
     cJSONUtils_CompareToPatch(patches, (const unsigned char*)"", from, to);
     cJSONUtils_CompareToPatch(patches, (const unsigned char*)"", from, to);
@@ -786,21 +787,21 @@ static cJSON *cJSONUtils_SortList(cJSON *list)
     return list;
     return list;
 }
 }
 
 
-void cJSONUtils_SortObject(cJSON *object)
+CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON *object)
 {
 {
     object->child = cJSONUtils_SortList(object->child);
     object->child = cJSONUtils_SortList(object->child);
 }
 }
 
 
-cJSON* cJSONUtils_MergePatch(cJSON *target, cJSON *patch)
+CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, cJSON *patch)
 {
 {
-    if (!patch || ((patch->type & 0xFF) != cJSON_Object))
+    if (!cJSON_IsObject(patch))
     {
     {
         /* scalar value, array or NULL, just duplicate */
         /* scalar value, array or NULL, just duplicate */
         cJSON_Delete(target);
         cJSON_Delete(target);
         return cJSON_Duplicate(patch, 1);
         return cJSON_Duplicate(patch, 1);
     }
     }
 
 
-    if (!target || ((target->type & 0xFF) != cJSON_Object))
+    if (!cJSON_IsObject(target))
     {
     {
         cJSON_Delete(target);
         cJSON_Delete(target);
         target = cJSON_CreateObject();
         target = cJSON_CreateObject();
@@ -809,7 +810,7 @@ cJSON* cJSONUtils_MergePatch(cJSON *target, cJSON *patch)
     patch = patch->child;
     patch = patch->child;
     while (patch)
     while (patch)
     {
     {
-        if ((patch->type & 0xFF) == cJSON_NULL)
+        if (cJSON_IsNull(patch))
         {
         {
             /* NULL is the indicator to remove a value, see RFC7396 */
             /* NULL is the indicator to remove a value, see RFC7396 */
             cJSON_DeleteItemFromObject(target, patch->string);
             cJSON_DeleteItemFromObject(target, patch->string);
@@ -824,7 +825,7 @@ cJSON* cJSONUtils_MergePatch(cJSON *target, cJSON *patch)
     return target;
     return target;
 }
 }
 
 
-cJSON *cJSONUtils_GenerateMergePatch(cJSON *from, cJSON *to)
+CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON *from, cJSON *to)
 {
 {
     cJSON *patch = NULL;
     cJSON *patch = NULL;
     if (!to)
     if (!to)
@@ -832,7 +833,7 @@ cJSON *cJSONUtils_GenerateMergePatch(cJSON *from, cJSON *to)
         /* patch to delete everything */
         /* patch to delete everything */
         return cJSON_CreateNull();
         return cJSON_CreateNull();
     }
     }
-    if (((to->type & 0xFF) != cJSON_Object) || !from || ((from->type & 0xFF) != cJSON_Object))
+    if (!cJSON_IsObject(to) || !cJSON_IsObject(from))
     {
     {
         return cJSON_Duplicate(to, 1);
         return cJSON_Duplicate(to, 1);
     }
     }

+ 8 - 8
cJSON_Utils.h

@@ -1,14 +1,14 @@
 #include "cJSON.h"
 #include "cJSON.h"
 
 
 /* Implement RFC6901 (https://tools.ietf.org/html/rfc6901) JSON Pointer spec. */
 /* Implement RFC6901 (https://tools.ietf.org/html/rfc6901) JSON Pointer spec. */
-cJSON *cJSONUtils_GetPointer(cJSON *object, const char *pointer);
+CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointer(cJSON *object, const char *pointer);
 
 
 /* Implement RFC6902 (https://tools.ietf.org/html/rfc6902) JSON Patch spec. */
 /* Implement RFC6902 (https://tools.ietf.org/html/rfc6902) JSON Patch spec. */
-cJSON* cJSONUtils_GeneratePatches(cJSON *from, cJSON *to);
+CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON *from, cJSON *to);
 /* Utility for generating patch array entries. */
 /* Utility for generating patch array entries. */
-void cJSONUtils_AddPatchToArray(cJSON *array, const char *op, const char *path, cJSON *val);
+CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON *array, const char *op, const char *path, cJSON *val);
 /* Returns 0 for success. */
 /* Returns 0 for success. */
-int cJSONUtils_ApplyPatches(cJSON *object, cJSON *patches);
+CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON *object, cJSON *patches);
 
 
 /*
 /*
 // Note that ApplyPatches is NOT atomic on failure. To implement an atomic ApplyPatches, use:
 // Note that ApplyPatches is NOT atomic on failure. To implement an atomic ApplyPatches, use:
@@ -33,12 +33,12 @@ int cJSONUtils_ApplyPatches(cJSON *object, cJSON *patches);
 
 
 /* Implement RFC7386 (https://tools.ietf.org/html/rfc7396) JSON Merge Patch spec. */
 /* Implement RFC7386 (https://tools.ietf.org/html/rfc7396) JSON Merge Patch spec. */
 /* target will be modified by patch. return value is new ptr for target. */
 /* target will be modified by patch. return value is new ptr for target. */
-cJSON* cJSONUtils_MergePatch(cJSON *target, cJSON *patch);
+CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, cJSON *patch);
 /* generates a patch to move from -> to */
 /* generates a patch to move from -> to */
-cJSON *cJSONUtils_GenerateMergePatch(cJSON *from, cJSON *to);
+CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON *from, cJSON *to);
 
 
 /* Given a root object and a target object, construct a pointer from one to the other. */
 /* Given a root object and a target object, construct a pointer from one to the other. */
-char *cJSONUtils_FindPointerFromObjectTo(cJSON *object, cJSON *target);
+CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(cJSON *object, cJSON *target);
 
 
 /* Sorts the members of the object into alphabetical order. */
 /* Sorts the members of the object into alphabetical order. */
-void cJSONUtils_SortObject(cJSON *object);
+CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON *object);

+ 1 - 0
fuzzing/.gitignore

@@ -0,0 +1 @@
+afl-build

+ 28 - 0
fuzzing/CMakeLists.txt

@@ -0,0 +1,28 @@
+option(ENABLE_FUZZING "Create executables and targets for fuzzing cJSON with afl." Off)
+if (ENABLE_FUZZING)
+    find_program(AFL_FUZZ afl-fuzz)
+    if ("${AFL_FUZZ}" MATCHES "AFL_FUZZ-NOTFOUND")
+        message(FATAL_ERROR "Couldn't find afl-fuzz.")
+    endif()
+
+    add_executable(afl-main afl.c)
+    target_link_libraries(afl-main "${CJSON_LIB}")
+
+    if (NOT ENABLE_SANITIZERS)
+        message(FATAL_ERROR "Enable sanitizers with -DENABLE_SANITIZERS=On to do fuzzing.")
+    endif()
+
+    option(ENABLE_FUZZING_PRINT "Fuzz printing functions together with parser." On)
+    set(fuzz_print_parameter "no")
+    if (ENABLE_FUZZING_PRINT)
+        set(fuzz_print_parameter "yes")
+    endif()
+
+    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-error")
+
+    add_custom_target(afl
+        COMMAND "${AFL_FUZZ}" -i "${CMAKE_CURRENT_SOURCE_DIR}/inputs" -o "${CMAKE_CURRENT_BINARY_DIR}/findings" -x "${CMAKE_CURRENT_SOURCE_DIR}/json.dict" -- "${CMAKE_CURRENT_BINARY_DIR}/afl-main" "@@" "${fuzz_print_parameter}"
+        DEPENDS afl-main)
+
+
+endif()

+ 5 - 0
fuzzing/afl-prepare-linux.sh

@@ -0,0 +1,5 @@
+#!/bin/bash
+
+set -x
+echo core | sudo tee /proc/sys/kernel/core_pattern
+echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor

+ 176 - 0
fuzzing/afl.c

@@ -0,0 +1,176 @@
+/*
+  Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "../cJSON.h"
+
+static char *read_file(const char *filename)
+{
+    FILE *file = NULL;
+    long length = 0;
+    char *content = NULL;
+    size_t read_chars = 0;
+
+    /* open in read binary mode */
+    file = fopen(filename, "rb");
+    if (file == NULL)
+    {
+        goto cleanup;
+    }
+
+    /* get the length */
+    if (fseek(file, 0, SEEK_END) != 0)
+    {
+        goto cleanup;
+    }
+    length = ftell(file);
+    if (length < 0)
+    {
+        goto cleanup;
+    }
+    if (fseek(file, 0, SEEK_SET) != 0)
+    {
+        goto cleanup;
+    }
+
+    /* allocate content buffer */
+    content = (char*)malloc((size_t)length + sizeof('\0'));
+    if (content == NULL)
+    {
+        goto cleanup;
+    }
+
+    /* read the file into memory */
+    read_chars = fread(content, sizeof(char), (size_t)length, file);
+    if ((long)read_chars != length)
+    {
+        free(content);
+        content = NULL;
+        goto cleanup;
+    }
+    content[read_chars] = '\0';
+
+
+cleanup:
+    if (file != NULL)
+    {
+        fclose(file);
+    }
+
+    return content;
+}
+
+int main(int argc, char** argv)
+{
+    const char *filename = NULL;
+    cJSON *item = NULL;
+    char *json = NULL;
+    int status = EXIT_FAILURE;
+    char *printed_json = NULL;
+
+    if ((argc < 2) || (argc > 3))
+    {
+        printf("Usage:\n");
+        printf("%s input_file [enable_printing]\n", argv[0]);
+        printf("\t input_file: file containing the test data\n");
+        printf("\t enable_printing: print after parsing, 'yes' or 'no', defaults to 'no'\n");
+        goto cleanup;
+    }
+
+    filename = argv[1];
+
+#if __AFL_HAVE_MANUAL_CONTROL
+    while (__AFL_LOOP(1000))
+    {
+#endif
+    status = EXIT_SUCCESS;
+
+    json = read_file(filename);
+    if ((json == NULL) || (json[0] == '\0') || (json[1] == '\0'))
+    {
+        status = EXIT_FAILURE;
+        goto cleanup;
+    }
+    item = cJSON_Parse(json + 2);
+    if (item == NULL)
+    {
+        goto cleanup;
+    }
+
+    if ((argc == 3) && (strncmp(argv[2], "yes", 3) == 0))
+    {
+        int do_format = 0;
+        if (json[1] == 'f')
+        {
+            do_format = 1;
+        }
+
+        if (json[0] == 'b')
+        {
+            /* buffered printing */
+            printed_json = cJSON_PrintBuffered(item, 1, do_format);
+        }
+        else
+        {
+            /* unbuffered printing */
+            if (do_format)
+            {
+                printed_json = cJSON_Print(item);
+            }
+            else
+            {
+                printed_json = cJSON_PrintUnformatted(item);
+            }
+        }
+        if (printed_json == NULL)
+        {
+            status = EXIT_FAILURE;
+            goto cleanup;
+        }
+        printf("%s\n", printed_json);
+    }
+
+cleanup:
+    if (item != NULL)
+    {
+        cJSON_Delete(item);
+        item = NULL;
+    }
+    if (json != NULL)
+    {
+        free(json);
+        json = NULL;
+    }
+    if (printed_json != NULL)
+    {
+        free(printed_json);
+        printed_json = NULL;
+    }
+#if __AFL_HAVE_MANUAL_CONTROL
+    }
+#endif
+
+    return status;
+}

+ 9 - 0
fuzzing/afl.sh

@@ -0,0 +1,9 @@
+#!/bin/bash
+
+mkdir -p afl-build || exit 1
+cd afl-build || exit 1
+#cleanup
+rm -r -- *
+
+CC=afl-clang-fast cmake ../.. -DENABLE_FUZZING=On -DENABLE_SANITIZERS=On -DBUILD_SHARED_LIBS=Off
+make afl

+ 22 - 0
fuzzing/inputs/test1

@@ -0,0 +1,22 @@
+bf{
+    "glossary": {
+        "title": "example glossary",
+		"GlossDiv": {
+            "title": "S",
+			"GlossList": {
+                "GlossEntry": {
+                    "ID": "SGML",
+					"SortAs": "SGML",
+					"GlossTerm": "Standard Generalized Markup Language",
+					"Acronym": "SGML",
+					"Abbrev": "ISO 8879:1986",
+					"GlossDef": {
+                        "para": "A meta-markup language, used to create markup languages such as DocBook.",
+						"GlossSeeAlso": ["GML", "XML"]
+                    },
+					"GlossSee": "markup"
+                }
+            }
+        }
+    }
+}

+ 1 - 0
fuzzing/inputs/test10

@@ -0,0 +1 @@
+bf["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]

+ 8 - 0
fuzzing/inputs/test11

@@ -0,0 +1,8 @@
+bf{
+"name": "Jack (\"Bee\") Nimble", 
+"format": {"type":       "rect", 
+"width":      1920, 
+"height":     1080, 
+"interlace":  false,"frame rate": 24
+}
+}

+ 11 - 0
fuzzing/inputs/test2

@@ -0,0 +1,11 @@
+bf{"menu": {
+  "id": "file",
+  "value": "File",
+  "popup": {
+    "menuitem": [
+      {"value": "New", "onclick": "CreateNewDoc()"},
+      {"value": "Open", "onclick": "OpenDoc()"},
+      {"value": "Close", "onclick": "CloseDoc()"}
+    ]
+  }
+}}

+ 26 - 0
fuzzing/inputs/test3

@@ -0,0 +1,26 @@
+bf{"widget": {
+    "debug": "on",
+    "window": {
+        "title": "Sample Konfabulator Widget",
+        "name": "main_window",
+        "width": 500,
+        "height": 500
+    },
+    "image": { 
+        "src": "Images/Sun.png",
+        "name": "sun1",
+        "hOffset": 250,
+        "vOffset": 250,
+        "alignment": "center"
+    },
+    "text": {
+        "data": "Click Here",
+        "size": 36,
+        "style": "bold",
+        "name": "text1",
+        "hOffset": 250,
+        "vOffset": 100,
+        "alignment": "center",
+        "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
+    }
+}}    

+ 26 - 0
fuzzing/inputs/test3.bu

@@ -0,0 +1,26 @@
+bu{"widget": {
+    "debug": "on",
+    "window": {
+        "title": "Sample Konfabulator Widget",
+        "name": "main_window",
+        "width": 500,
+        "height": 500
+    },
+    "image": { 
+        "src": "Images/Sun.png",
+        "name": "sun1",
+        "hOffset": 250,
+        "vOffset": 250,
+        "alignment": "center"
+    },
+    "text": {
+        "data": "Click Here",
+        "size": 36,
+        "style": "bold",
+        "name": "text1",
+        "hOffset": 250,
+        "vOffset": 100,
+        "alignment": "center",
+        "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
+    }
+}}    

+ 26 - 0
fuzzing/inputs/test3.uf

@@ -0,0 +1,26 @@
+uf{"widget": {
+    "debug": "on",
+    "window": {
+        "title": "Sample Konfabulator Widget",
+        "name": "main_window",
+        "width": 500,
+        "height": 500
+    },
+    "image": { 
+        "src": "Images/Sun.png",
+        "name": "sun1",
+        "hOffset": 250,
+        "vOffset": 250,
+        "alignment": "center"
+    },
+    "text": {
+        "data": "Click Here",
+        "size": 36,
+        "style": "bold",
+        "name": "text1",
+        "hOffset": 250,
+        "vOffset": 100,
+        "alignment": "center",
+        "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
+    }
+}}    

+ 26 - 0
fuzzing/inputs/test3.uu

@@ -0,0 +1,26 @@
+uu{"widget": {
+    "debug": "on",
+    "window": {
+        "title": "Sample Konfabulator Widget",
+        "name": "main_window",
+        "width": 500,
+        "height": 500
+    },
+    "image": { 
+        "src": "Images/Sun.png",
+        "name": "sun1",
+        "hOffset": 250,
+        "vOffset": 250,
+        "alignment": "center"
+    },
+    "text": {
+        "data": "Click Here",
+        "size": 36,
+        "style": "bold",
+        "name": "text1",
+        "hOffset": 250,
+        "vOffset": 100,
+        "alignment": "center",
+        "onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
+    }
+}}    

+ 88 - 0
fuzzing/inputs/test4

@@ -0,0 +1,88 @@
+bf{"web-app": {
+  "servlet": [   
+    {
+      "servlet-name": "cofaxCDS",
+      "servlet-class": "org.cofax.cds.CDSServlet",
+      "init-param": {
+        "configGlossary:installationAt": "Philadelphia, PA",
+        "configGlossary:adminEmail": "ksm@pobox.com",
+        "configGlossary:poweredBy": "Cofax",
+        "configGlossary:poweredByIcon": "/images/cofax.gif",
+        "configGlossary:staticPath": "/content/static",
+        "templateProcessorClass": "org.cofax.WysiwygTemplate",
+        "templateLoaderClass": "org.cofax.FilesTemplateLoader",
+        "templatePath": "templates",
+        "templateOverridePath": "",
+        "defaultListTemplate": "listTemplate.htm",
+        "defaultFileTemplate": "articleTemplate.htm",
+        "useJSP": false,
+        "jspListTemplate": "listTemplate.jsp",
+        "jspFileTemplate": "articleTemplate.jsp",
+        "cachePackageTagsTrack": 200,
+        "cachePackageTagsStore": 200,
+        "cachePackageTagsRefresh": 60,
+        "cacheTemplatesTrack": 100,
+        "cacheTemplatesStore": 50,
+        "cacheTemplatesRefresh": 15,
+        "cachePagesTrack": 200,
+        "cachePagesStore": 100,
+        "cachePagesRefresh": 10,
+        "cachePagesDirtyRead": 10,
+        "searchEngineListTemplate": "forSearchEnginesList.htm",
+        "searchEngineFileTemplate": "forSearchEngines.htm",
+        "searchEngineRobotsDb": "WEB-INF/robots.db",
+        "useDataStore": true,
+        "dataStoreClass": "org.cofax.SqlDataStore",
+        "redirectionClass": "org.cofax.SqlRedirection",
+        "dataStoreName": "cofax",
+        "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver",
+        "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon",
+        "dataStoreUser": "sa",
+        "dataStorePassword": "dataStoreTestQuery",
+        "dataStoreTestQuery": "SET NOCOUNT ON;select test='test';",
+        "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log",
+        "dataStoreInitConns": 10,
+        "dataStoreMaxConns": 100,
+        "dataStoreConnUsageLimit": 100,
+        "dataStoreLogLevel": "debug",
+        "maxUrlLength": 500}},
+    {
+      "servlet-name": "cofaxEmail",
+      "servlet-class": "org.cofax.cds.EmailServlet",
+      "init-param": {
+      "mailHost": "mail1",
+      "mailHostOverride": "mail2"}},
+    {
+      "servlet-name": "cofaxAdmin",
+      "servlet-class": "org.cofax.cds.AdminServlet"},
+ 
+    {
+      "servlet-name": "fileServlet",
+      "servlet-class": "org.cofax.cds.FileServlet"},
+    {
+      "servlet-name": "cofaxTools",
+      "servlet-class": "org.cofax.cms.CofaxToolsServlet",
+      "init-param": {
+        "templatePath": "toolstemplates/",
+        "log": 1,
+        "logLocation": "/usr/local/tomcat/logs/CofaxTools.log",
+        "logMaxSize": "",
+        "dataLog": 1,
+        "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log",
+        "dataLogMaxSize": "",
+        "removePageCache": "/content/admin/remove?cache=pages&id=",
+        "removeTemplateCache": "/content/admin/remove?cache=templates&id=",
+        "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder",
+        "lookInContext": 1,
+        "adminGroupID": 4,
+        "betaServer": true}}],
+  "servlet-mapping": {
+    "cofaxCDS": "/",
+    "cofaxEmail": "/cofaxutil/aemail/*",
+    "cofaxAdmin": "/admin/*",
+    "fileServlet": "/static/*",
+    "cofaxTools": "/tools/*"},
+ 
+  "taglib": {
+    "taglib-uri": "cofax.tld",
+    "taglib-location": "/WEB-INF/tlds/cofax.tld"}}}

+ 27 - 0
fuzzing/inputs/test5

@@ -0,0 +1,27 @@
+bf{"menu": {
+    "header": "SVG Viewer",
+    "items": [
+        {"id": "Open"},
+        {"id": "OpenNew", "label": "Open New"},
+        null,
+        {"id": "ZoomIn", "label": "Zoom In"},
+        {"id": "ZoomOut", "label": "Zoom Out"},
+        {"id": "OriginalView", "label": "Original View"},
+        null,
+        {"id": "Quality"},
+        {"id": "Pause"},
+        {"id": "Mute"},
+        null,
+        {"id": "Find", "label": "Find..."},
+        {"id": "FindAgain", "label": "Find Again"},
+        {"id": "Copy"},
+        {"id": "CopyAgain", "label": "Copy Again"},
+        {"id": "CopySVG", "label": "Copy SVG"},
+        {"id": "ViewSVG", "label": "View SVG"},
+        {"id": "ViewSource", "label": "View Source"},
+        {"id": "SaveAs", "label": "Save As"},
+        null,
+        {"id": "Help"},
+        {"id": "About", "label": "About Adobe CVG Viewer..."}
+    ]
+}}

+ 16 - 0
fuzzing/inputs/test6

@@ -0,0 +1,16 @@
+bf<!DOCTYPE html>
+    <html>
+    <head>
+      <meta name="viewport" content="width=device-width, initial-scale=1">
+      <style type="text/css">
+        html, body, iframe { margin: 0; padding: 0; height: 100%; }
+        iframe { display: block; width: 100%; border: none; }
+      </style>
+    <title>Application Error</title>
+    </head>
+    <body>
+      <iframe src="//s3.amazonaws.com/heroku_pages/error.html">
+        <p>Application Error</p>
+      </iframe>
+    </body>
+    </html>

+ 22 - 0
fuzzing/inputs/test7

@@ -0,0 +1,22 @@
+bf[
+	 {
+	 "precision": "zip",
+	 "Latitude":  37.7668,
+	 "Longitude": -122.3959,
+	 "Address":   "",
+	 "City":      "SAN FRANCISCO",
+	 "State":     "CA",
+	 "Zip":       "94107",
+	 "Country":   "US"
+	 },
+	 {
+	 "precision": "zip",
+	 "Latitude":  37.371991,
+	 "Longitude": -122.026020,
+	 "Address":   "",
+	 "City":      "SUNNYVALE",
+	 "State":     "CA",
+	 "Zip":       "94085",
+	 "Country":   "US"
+	 }
+	 ]

+ 13 - 0
fuzzing/inputs/test8

@@ -0,0 +1,13 @@
+bf{
+		"Image": {
+			"Width":  800,
+			"Height": 600,
+			"Title":  "View from 15th Floor",
+			"Thumbnail": {
+				"Url":    "http:/*www.example.com/image/481989943",
+				"Height": 125,
+				"Width":  "100"
+			},
+			"IDs": [116, 943, 234, 38793]
+		}
+	}

+ 5 - 0
fuzzing/inputs/test9

@@ -0,0 +1,5 @@
+bf[
+    [0, -1, 0],
+    [1, 0, 0],
+    [0, 0, 1]
+	]

+ 47 - 0
fuzzing/json.dict

@@ -0,0 +1,47 @@
+#
+# AFL dictionary for JSON
+# -----------------------------
+#
+
+object_start="{"
+object_end="}"
+object_empty="{}"
+object_one_element="{\"one\":1}"
+object_two_elements="{\"1\":1,\"2\":2}"
+object_separator=":"
+
+array_start="["
+array_end="]"
+array_empty="[]"
+array_one_element="[1]"
+array_two_elements="[1,2]"
+
+separator=","
+
+escape_sequence_b="\\b"
+escape_sequence_f="\\f"
+escape_sequence_n="\\n"
+escape_sequence_r="\\r"
+escape_sequence_t="\\t"
+escape_sequence_quote="\\\""
+escape_sequence_backslash="\\\\"
+escapce_sequence_slash="\\/"
+escpae_sequence_utf16_base="\\u"
+escape_sequence_utf16="\\u12ab"
+
+number_integer="1"
+number_double="1.0"
+number_negative_integer="-1"
+number_negative_double="-1.0"
+number_engineering1="1e1"
+number_engineering2="1e-1"
+number_positive_integer="+1"
+number_positive_double="+1.0"
+number_e="e"
+number_plus="+"
+number_minus="-"
+number_separator="."
+
+null="null"
+true="true"
+false="false"

+ 22 - 0
tests/CMakeLists.txt

@@ -1,6 +1,17 @@
 if(ENABLE_CJSON_TEST)
 if(ENABLE_CJSON_TEST)
     add_library(unity unity/src/unity.c)
     add_library(unity unity/src/unity.c)
 
 
+    # Disable -Werror for Unity
+    list(FIND custom_compiler_flags "-Werror" werror_found)
+    if (werror_found)
+        target_compile_options(unity PRIVATE "-Wno-error")
+    endif()
+    # Disable -fvisibility=hidden for Unity
+    list(FIND custom_compiler_flags "-fvisibility=hidden" visibility_found)
+    if (visibility_found)
+        target_compile_options(unity PRIVATE "-fvisibility=default")
+    endif()
+
     #copy test files
     #copy test files
     file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/inputs")
     file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/inputs")
     file(GLOB test_files "inputs/*")
     file(GLOB test_files "inputs/*")
@@ -14,6 +25,12 @@ if(ENABLE_CJSON_TEST)
         parse_array
         parse_array
         parse_object
         parse_object
         parse_value
         parse_value
+        print_string
+        print_number
+        print_array
+        print_object
+        print_value
+        misc_tests
     )
     )
 
 
     add_library(test-common common.c)
     add_library(test-common common.c)
@@ -29,6 +46,11 @@ if(ENABLE_CJSON_TEST)
         endif()
         endif()
     endif()
     endif()
 
 
+    #"check" target that automatically builds everything and runs the tests
+    add_custom_target(check 
+        COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure
+        DEPENDS ${unity_tests})
+
     foreach(unity_test ${unity_tests})
     foreach(unity_test ${unity_tests})
         add_executable("${unity_test}" "${unity_test}.c")
         add_executable("${unity_test}" "${unity_test}.c")
         target_link_libraries("${unity_test}" "${CJSON_LIB}" unity test-common)
         target_link_libraries("${unity_test}" "${CJSON_LIB}" unity test-common)

+ 4 - 4
tests/common.c

@@ -22,7 +22,7 @@
 
 
 #include "common.h"
 #include "common.h"
 
 
-extern void reset(cJSON *item)
+CJSON_PUBLIC(void) reset(cJSON *item)
 {
 {
     if ((item != NULL) && (item->child != NULL))
     if ((item != NULL) && (item->child != NULL))
     {
     {
@@ -30,17 +30,17 @@ extern void reset(cJSON *item)
     }
     }
     if ((item->valuestring != NULL) && !(item->type & cJSON_IsReference))
     if ((item->valuestring != NULL) && !(item->type & cJSON_IsReference))
     {
     {
-        cJSON_free(item->valuestring);
+        global_hooks.deallocate(item->valuestring);
     }
     }
     if ((item->string != NULL) && !(item->type & cJSON_StringIsConst))
     if ((item->string != NULL) && !(item->type & cJSON_StringIsConst))
     {
     {
-        cJSON_free(item->string);
+        global_hooks.deallocate(item->string);
     }
     }
 
 
     memset(item, 0, sizeof(cJSON));
     memset(item, 0, sizeof(cJSON));
 }
 }
 
 
-extern char *read_file(const char *filename)
+CJSON_PUBLIC(char*) read_file(const char *filename)
 {
 {
     FILE *file = NULL;
     FILE *file = NULL;
     long length = 0;
     long length = 0;

+ 2 - 3
tests/common.h

@@ -25,9 +25,8 @@
 
 
 #include "../cJSON.c"
 #include "../cJSON.c"
 
 
-extern void reset(cJSON *item);
-extern char *read_file(const char *filename);
-extern cjbool assert_is_invalid(cJSON *item);
+CJSON_PUBLIC(void) reset(cJSON *item);
+CJSON_PUBLIC(char*) read_file(const char *filename);
 
 
 /* assertion helper macros */
 /* assertion helper macros */
 #define assert_has_type(item, item_type) TEST_ASSERT_BITS_MESSAGE(0xFF, item_type, item->type, "Item doesn't have expected type.")
 #define assert_has_type(item, item_type) TEST_ASSERT_BITS_MESSAGE(0xFF, item_type, item->type, "Item doesn't have expected type.")

+ 3 - 3
tests/inputs/test7.expected

@@ -1,7 +1,7 @@
 [{
 [{
 		"precision":	"zip",
 		"precision":	"zip",
-		"Latitude":	37.766800,
-		"Longitude":	-122.395900,
+		"Latitude":	37.7668,
+		"Longitude":	-122.3959,
 		"Address":	"",
 		"Address":	"",
 		"City":	"SAN FRANCISCO",
 		"City":	"SAN FRANCISCO",
 		"State":	"CA",
 		"State":	"CA",
@@ -10,7 +10,7 @@
 	}, {
 	}, {
 		"precision":	"zip",
 		"precision":	"zip",
 		"Latitude":	37.371991,
 		"Latitude":	37.371991,
-		"Longitude":	-122.026020,
+		"Longitude":	-122.02602,
 		"Address":	"",
 		"Address":	"",
 		"City":	"SUNNYVALE",
 		"City":	"SUNNYVALE",
 		"State":	"CA",
 		"State":	"CA",

+ 197 - 0
tests/misc_tests.c

@@ -0,0 +1,197 @@
+/*
+  Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "unity/examples/unity_config.h"
+#include "unity/src/unity.h"
+#include "common.h"
+
+
+static void cjson_array_foreach_should_loop_over_arrays(void)
+{
+    cJSON array[1];
+    cJSON elements[10];
+    cJSON *element_pointer = NULL;
+    size_t i = 0;
+
+    memset(array, 0, sizeof(array));
+    memset(elements, 0, sizeof(elements));
+
+    /* create array */
+    array[0].child = &elements[0];
+    elements[0].prev = NULL;
+    elements[9].next = NULL;
+    for (i = 0; i < 9; i++)
+    {
+        elements[i].next = &elements[i + 1];
+        elements[i + 1].prev = &elements[i];
+    }
+
+    i = 0;
+    cJSON_ArrayForEach(element_pointer, array)
+    {
+        TEST_ASSERT_TRUE_MESSAGE(element_pointer == &elements[i], "Not iterating over array properly");
+        i++;
+    }
+}
+
+static void cjson_array_foreach_should_not_dereference_null_pointer(void)
+{
+    cJSON *array = NULL;
+    cJSON *element = NULL;
+    cJSON_ArrayForEach(element, array);
+}
+
+static void cjson_get_object_item_should_get_object_items(void)
+{
+    cJSON *item = NULL;
+    cJSON *found = NULL;
+
+    item = cJSON_Parse("{\"one\":1, \"Two\":2, \"tHree\":3}");
+
+    found = cJSON_GetObjectItem(NULL, "test");
+    TEST_ASSERT_NULL_MESSAGE(found, "Failed to fail on NULL pointer.");
+
+    found = cJSON_GetObjectItem(item, NULL);
+    TEST_ASSERT_NULL_MESSAGE(found, "Failed to fail on NULL string.");
+
+
+    found = cJSON_GetObjectItem(item, "one");
+    TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find first item.");
+    TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 1);
+
+    found = cJSON_GetObjectItem(item, "tWo");
+    TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find first item.");
+    TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 2);
+
+    found = cJSON_GetObjectItem(item, "three");
+    TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find item.");
+    TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 3);
+
+    found = cJSON_GetObjectItem(item, "four");
+    TEST_ASSERT_NULL_MESSAGE(found, "Should not find something that isn't there.");
+
+    cJSON_Delete(item);
+}
+
+static void cjson_get_object_item_case_sensitive_should_get_object_items(void)
+{
+    cJSON *item = NULL;
+    cJSON *found = NULL;
+
+    item = cJSON_Parse("{\"one\":1, \"Two\":2, \"tHree\":3}");
+
+    found = cJSON_GetObjectItemCaseSensitive(NULL, "test");
+    TEST_ASSERT_NULL_MESSAGE(found, "Failed to fail on NULL pointer.");
+
+    found = cJSON_GetObjectItemCaseSensitive(item, NULL);
+    TEST_ASSERT_NULL_MESSAGE(found, "Failed to fail on NULL string.");
+
+    found = cJSON_GetObjectItemCaseSensitive(item, "one");
+    TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find first item.");
+    TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 1);
+
+    found = cJSON_GetObjectItemCaseSensitive(item, "Two");
+    TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find first item.");
+    TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 2);
+
+    found = cJSON_GetObjectItemCaseSensitive(item, "tHree");
+    TEST_ASSERT_NOT_NULL_MESSAGE(found, "Failed to find item.");
+    TEST_ASSERT_EQUAL_DOUBLE(found->valuedouble, 3);
+
+    found = cJSON_GetObjectItemCaseSensitive(item, "One");
+    TEST_ASSERT_NULL_MESSAGE(found, "Should not find something that isn't there.");
+
+    cJSON_Delete(item);
+}
+
+static void typecheck_functions_should_check_type(void)
+{
+    cJSON invalid[1];
+    cJSON item[1];
+    invalid->type = cJSON_Invalid;
+    invalid->type |= cJSON_StringIsConst;
+    item->type = cJSON_False;
+    item->type |= cJSON_StringIsConst;
+
+    TEST_ASSERT_FALSE(cJSON_IsInvalid(NULL));
+    TEST_ASSERT_FALSE(cJSON_IsInvalid(item));
+    TEST_ASSERT_TRUE(cJSON_IsInvalid(invalid));
+
+    item->type = cJSON_False | cJSON_StringIsConst;
+    TEST_ASSERT_FALSE(cJSON_IsFalse(NULL));
+    TEST_ASSERT_FALSE(cJSON_IsFalse(invalid));
+    TEST_ASSERT_TRUE(cJSON_IsFalse(item));
+    TEST_ASSERT_TRUE(cJSON_IsBool(item));
+
+    item->type = cJSON_True | cJSON_StringIsConst;
+    TEST_ASSERT_FALSE(cJSON_IsTrue(NULL));
+    TEST_ASSERT_FALSE(cJSON_IsTrue(invalid));
+    TEST_ASSERT_TRUE(cJSON_IsTrue(item));
+    TEST_ASSERT_TRUE(cJSON_IsBool(item));
+
+    item->type = cJSON_NULL | cJSON_StringIsConst;
+    TEST_ASSERT_FALSE(cJSON_IsNull(NULL));
+    TEST_ASSERT_FALSE(cJSON_IsNull(invalid));
+    TEST_ASSERT_TRUE(cJSON_IsNull(item));
+
+    item->type = cJSON_Number | cJSON_StringIsConst;
+    TEST_ASSERT_FALSE(cJSON_IsNumber(NULL));
+    TEST_ASSERT_FALSE(cJSON_IsNumber(invalid));
+    TEST_ASSERT_TRUE(cJSON_IsNumber(item));
+
+    item->type = cJSON_String | cJSON_StringIsConst;
+    TEST_ASSERT_FALSE(cJSON_IsString(NULL));
+    TEST_ASSERT_FALSE(cJSON_IsString(invalid));
+    TEST_ASSERT_TRUE(cJSON_IsString(item));
+
+    item->type = cJSON_Array | cJSON_StringIsConst;
+    TEST_ASSERT_FALSE(cJSON_IsArray(NULL));
+    TEST_ASSERT_FALSE(cJSON_IsArray(invalid));
+    TEST_ASSERT_TRUE(cJSON_IsArray(item));
+
+    item->type = cJSON_Object | cJSON_StringIsConst;
+    TEST_ASSERT_FALSE(cJSON_IsObject(NULL));
+    TEST_ASSERT_FALSE(cJSON_IsObject(invalid));
+    TEST_ASSERT_TRUE(cJSON_IsObject(item));
+
+    item->type = cJSON_Raw | cJSON_StringIsConst;
+    TEST_ASSERT_FALSE(cJSON_IsRaw(NULL));
+    TEST_ASSERT_FALSE(cJSON_IsRaw(invalid));
+    TEST_ASSERT_TRUE(cJSON_IsRaw(item));
+}
+
+int main(void)
+{
+    UNITY_BEGIN();
+
+    RUN_TEST(cjson_array_foreach_should_loop_over_arrays);
+    RUN_TEST(cjson_array_foreach_should_not_dereference_null_pointer);
+    RUN_TEST(cjson_get_object_item_should_get_object_items);
+    RUN_TEST(cjson_get_object_item_case_sensitive_should_get_object_items);
+    RUN_TEST(typecheck_functions_should_check_type);
+
+    return UNITY_END();
+}

+ 3 - 3
tests/parse_array.c

@@ -46,13 +46,13 @@ static void assert_is_array(cJSON *array_item)
 
 
 static void assert_not_array(const char *json)
 static void assert_not_array(const char *json)
 {
 {
-    TEST_ASSERT_NULL(parse_array(item, (const unsigned char*)json, &error_pointer));
+    TEST_ASSERT_NULL(parse_array(item, (const unsigned char*)json, &error_pointer, &global_hooks));
     assert_is_invalid(item);
     assert_is_invalid(item);
 }
 }
 
 
 static void assert_parse_array(const char *json)
 static void assert_parse_array(const char *json)
 {
 {
-    TEST_ASSERT_NOT_NULL(parse_array(item, (const unsigned char*)json, &error_pointer));
+    TEST_ASSERT_NOT_NULL(parse_array(item, (const unsigned char*)json, &error_pointer, &global_hooks));
     assert_is_array(item);
     assert_is_array(item);
 }
 }
 
 
@@ -124,7 +124,7 @@ static void parse_array_should_parse_arrays_with_multiple_elements(void)
                 i = 0;
                 i = 0;
                 (i < (sizeof(expected_types)/sizeof(int)))
                 (i < (sizeof(expected_types)/sizeof(int)))
                 && (node != NULL);
                 && (node != NULL);
-                i++, node = node->next)
+                (void)i++, node = node->next)
         {
         {
             TEST_ASSERT_BITS(0xFF, expected_types[i], node->type);
             TEST_ASSERT_BITS(0xFF, expected_types[i], node->type);
         }
         }

+ 5 - 5
tests/parse_object.c

@@ -54,14 +54,14 @@ static void assert_is_child(cJSON *child_item, const char *name, int type)
 
 
 static void assert_not_object(const char *json)
 static void assert_not_object(const char *json)
 {
 {
-    TEST_ASSERT_NULL(parse_object(item, (const unsigned char*)json, &error_pointer));
+    TEST_ASSERT_NULL(parse_object(item, (const unsigned char*)json, &error_pointer, &global_hooks));
     assert_is_invalid(item);
     assert_is_invalid(item);
     reset(item);
     reset(item);
 }
 }
 
 
 static void assert_parse_object(const char *json)
 static void assert_parse_object(const char *json)
 {
 {
-    TEST_ASSERT_NOT_NULL(parse_object(item, (const unsigned char*)json, &error_pointer));
+    TEST_ASSERT_NOT_NULL(parse_object(item, (const unsigned char*)json, &error_pointer, &global_hooks));
     assert_is_object(item);
     assert_is_object(item);
 }
 }
 
 
@@ -76,7 +76,7 @@ static void parse_object_should_parse_empty_objects(void)
     reset(item);
     reset(item);
 }
 }
 
 
-static void parse_array_should_parse_arrays_with_one_element(void)
+static void parse_object_should_parse_objects_with_one_element(void)
 {
 {
 
 
     assert_parse_object("{\"one\":1}");
     assert_parse_object("{\"one\":1}");
@@ -134,7 +134,7 @@ static void parse_object_should_parse_objects_with_multiple_elements(void)
                 i = 0;
                 i = 0;
                 (i < (sizeof(expected_types)/sizeof(int)))
                 (i < (sizeof(expected_types)/sizeof(int)))
                 && (node != NULL);
                 && (node != NULL);
-                i++, node = node->next)
+                (void)i++, node = node->next)
         {
         {
             assert_is_child(node, expected_names[i], expected_types[i]);
             assert_is_child(node, expected_names[i], expected_types[i]);
         }
         }
@@ -163,6 +163,6 @@ int main(void)
     RUN_TEST(parse_object_should_parse_empty_objects);
     RUN_TEST(parse_object_should_parse_empty_objects);
     RUN_TEST(parse_object_should_not_parse_non_objects);
     RUN_TEST(parse_object_should_not_parse_non_objects);
     RUN_TEST(parse_object_should_parse_objects_with_multiple_elements);
     RUN_TEST(parse_object_should_parse_objects_with_multiple_elements);
-    RUN_TEST(parse_array_should_parse_arrays_with_one_element);
+    RUN_TEST(parse_object_should_parse_objects_with_one_element);
     return UNITY_END();
     return UNITY_END();
 }
 }

+ 3 - 3
tests/parse_string.c

@@ -47,15 +47,15 @@ static void assert_is_string(cJSON *string_item)
 
 
 static void assert_parse_string(const char *string, const char *expected)
 static void assert_parse_string(const char *string, const char *expected)
 {
 {
-    TEST_ASSERT_NOT_NULL_MESSAGE(parse_string(item, (const unsigned char*)string, &error_pointer), "Couldn't parse string.");
+    TEST_ASSERT_NOT_NULL_MESSAGE(parse_string(item, (const unsigned char*)string, &error_pointer, &global_hooks), "Couldn't parse string.");
     assert_is_string(item);
     assert_is_string(item);
     TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, item->valuestring, "The parsed result isn't as expected.");
     TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, item->valuestring, "The parsed result isn't as expected.");
-    cJSON_free(item->valuestring);
+    global_hooks.deallocate(item->valuestring);
     item->valuestring = NULL;
     item->valuestring = NULL;
 }
 }
 
 
 #define assert_not_parse_string(string) \
 #define assert_not_parse_string(string) \
-    TEST_ASSERT_NULL_MESSAGE(parse_string(item, (const unsigned char*)string, &error_pointer), "Malformed string should not be accepted");\
+    TEST_ASSERT_NULL_MESSAGE(parse_string(item, (const unsigned char*)string, &error_pointer, &global_hooks), "Malformed string should not be accepted");\
     assert_is_invalid(item)
     assert_is_invalid(item)
 
 
 
 

+ 1 - 1
tests/parse_value.c

@@ -44,7 +44,7 @@ static void assert_is_value(cJSON *value_item, int type)
 
 
 static void assert_parse_value(const char *string, int type)
 static void assert_parse_value(const char *string, int type)
 {
 {
-    TEST_ASSERT_NOT_NULL(parse_value(item, (const unsigned char*)string, &error_pointer));
+    TEST_ASSERT_NOT_NULL(parse_value(item, (const unsigned char*)string, &error_pointer, &global_hooks));
     assert_is_value(item, type);
     assert_is_value(item, type);
 }
 }
 
 

+ 92 - 0
tests/print_array.c

@@ -0,0 +1,92 @@
+/*
+  Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#include "unity/examples/unity_config.h"
+#include "unity/src/unity.h"
+#include "common.h"
+
+static void assert_print_array(const char * const expected, const char * const input)
+{
+    unsigned char printed_unformatted[1024];
+    unsigned char printed_formatted[1024];
+
+    const unsigned char *error_pointer;
+    cJSON item[1];
+
+    printbuffer formatted_buffer;
+    printbuffer unformatted_buffer;
+
+    /* buffer for formatted printing */
+    formatted_buffer.buffer = printed_formatted;
+    formatted_buffer.length = sizeof(printed_formatted);
+    formatted_buffer.offset = 0;
+    formatted_buffer.noalloc = true;
+
+    /* buffer for unformatted printing */
+    unformatted_buffer.buffer = printed_unformatted;
+    unformatted_buffer.length = sizeof(printed_unformatted);
+    unformatted_buffer.offset = 0;
+    unformatted_buffer.noalloc = true;
+
+    memset(item, 0, sizeof(item));
+    TEST_ASSERT_NOT_NULL_MESSAGE(parse_array(item, (const unsigned char*)input, &error_pointer, &global_hooks), "Failed to parse array.");
+
+    TEST_ASSERT_TRUE_MESSAGE(print_array(item, 0, false, &unformatted_buffer, &global_hooks), "Failed to print unformatted string.");
+    TEST_ASSERT_EQUAL_STRING_MESSAGE(input, printed_unformatted, "Unformatted array is not correct.");
+
+    TEST_ASSERT_TRUE_MESSAGE(print_array(item, 0, true, &formatted_buffer, &global_hooks), "Failed to print formatted string.");
+    TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, printed_formatted, "Formatted array is not correct.");
+
+    reset(item);
+}
+
+static void print_array_should_print_empty_arrays(void)
+{
+    assert_print_array("[]", "[]");
+}
+
+static void print_array_should_print_arrays_with_one_element(void)
+{
+
+    assert_print_array("[1]", "[1]");
+    assert_print_array("[\"hello!\"]", "[\"hello!\"]");
+    assert_print_array("[[]]", "[[]]");
+    assert_print_array("[null]", "[null]");
+}
+
+static void print_array_should_print_arrays_with_multiple_elements(void)
+{
+    assert_print_array("[1, 2, 3]", "[1,2,3]");
+    assert_print_array("[1, null, true, false, [], \"hello\", {\n\t}]", "[1,null,true,false,[],\"hello\",{}]");
+}
+
+int main(void)
+{
+    /* initialize cJSON item */
+    UNITY_BEGIN();
+
+    RUN_TEST(print_array_should_print_empty_arrays);
+    RUN_TEST(print_array_should_print_arrays_with_one_element);
+    RUN_TEST(print_array_should_print_arrays_with_multiple_elements);
+
+    return UNITY_END();
+}

+ 119 - 0
tests/print_number.c

@@ -0,0 +1,119 @@
+/*
+  Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#include "unity/examples/unity_config.h"
+#include "unity/src/unity.h"
+#include "common.h"
+
+static void assert_print_number(const char *expected, double input)
+{
+    unsigned char printed[1024];
+    cJSON item[1];
+    printbuffer buffer;
+    buffer.buffer = printed;
+    buffer.length = sizeof(printed);
+    buffer.offset = 0;
+    buffer.noalloc = true;
+
+    memset(item, 0, sizeof(item));
+    cJSON_SetNumberValue(item, input);
+
+    TEST_ASSERT_TRUE_MESSAGE(print_number(item, &buffer, &global_hooks), "Failed to print number.");
+    TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, buffer.buffer, "Printed number is not as expected.");
+}
+
+static void print_number_should_print_zero(void)
+{
+    assert_print_number("0", 0);
+}
+
+static void print_number_should_print_negative_integers(void)
+{
+    assert_print_number("-1", -1);
+    assert_print_number("-32768", -32768);
+    assert_print_number("-2147483648", -2147483648);
+}
+
+static void print_number_should_print_positive_integers(void)
+{
+    assert_print_number("1", 1);
+    assert_print_number("32767", 32767);
+    assert_print_number("2147483647", 2147483647);
+}
+
+static void print_number_should_print_positive_reals(void)
+{
+    assert_print_number("0.123", 0.123);
+    assert_print_number("1.000000e-09", 10e-10);
+    assert_print_number("1000000000000", 10e11);
+    assert_print_number("1.230000e+129", 123e+127);
+    assert_print_number("0", 123e-128); /* TODO: Maybe this shouldn't be 0 */
+}
+
+static void print_number_should_print_negative_reals(void)
+{
+    assert_print_number("-0.0123", -0.0123);
+    assert_print_number("-1.000000e-09", -10e-10);
+    assert_print_number("-1000000000000000000000", -10e20);
+    assert_print_number("-1.230000e+129", -123e+127);
+    assert_print_number("-1.230000e-126", -123e-128);
+}
+
+static void print_number_should_print_non_number(void)
+{
+    TEST_IGNORE();
+    /* FIXME: Cannot test this easily in C89! */
+    /* assert_print_number("null", NaN); */
+    /* assert_print_number("null", INFTY); */
+    /* assert_print_number("null", -INFTY); */
+}
+
+static void trim_trailing_zeroes_should_trim_trailing_zeroes(void)
+{
+    printbuffer buffer;
+    unsigned char number[100];
+    buffer.length = sizeof(number);
+    buffer.buffer = number;
+
+    strcpy((char*)number, "10.00");
+    buffer.offset = sizeof("10.00") - 1;
+    TEST_ASSERT_TRUE(trim_trailing_zeroes(&buffer));
+    TEST_ASSERT_EQUAL_UINT8('\0', buffer.buffer[buffer.offset]);
+    TEST_ASSERT_EQUAL_STRING("10", number);
+    TEST_ASSERT_EQUAL_UINT(sizeof("10") - 1, buffer.offset);
+}
+
+int main(void)
+{
+    /* initialize cJSON item */
+    UNITY_BEGIN();
+
+    RUN_TEST(print_number_should_print_zero);
+    RUN_TEST(print_number_should_print_negative_integers);
+    RUN_TEST(print_number_should_print_positive_integers);
+    RUN_TEST(print_number_should_print_positive_reals);
+    RUN_TEST(print_number_should_print_negative_reals);
+    RUN_TEST(print_number_should_print_non_number);
+    RUN_TEST(trim_trailing_zeroes_should_trim_trailing_zeroes);
+
+    return UNITY_END();
+}

+ 92 - 0
tests/print_object.c

@@ -0,0 +1,92 @@
+/*
+  Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#include "unity/examples/unity_config.h"
+#include "unity/src/unity.h"
+#include "common.h"
+
+static void assert_print_object(const char * const expected, const char * const input)
+{
+    unsigned char printed_unformatted[1024];
+    unsigned char printed_formatted[1024];
+
+    const unsigned char *error_pointer;
+    cJSON item[1];
+
+    printbuffer formatted_buffer;
+    printbuffer unformatted_buffer;
+
+    /* buffer for formatted printing */
+    formatted_buffer.buffer = printed_formatted;
+    formatted_buffer.length = sizeof(printed_formatted);
+    formatted_buffer.offset = 0;
+    formatted_buffer.noalloc = true;
+
+    /* buffer for unformatted printing */
+    unformatted_buffer.buffer = printed_unformatted;
+    unformatted_buffer.length = sizeof(printed_unformatted);
+    unformatted_buffer.offset = 0;
+    unformatted_buffer.noalloc = true;
+
+    memset(item, 0, sizeof(item));
+    TEST_ASSERT_NOT_NULL_MESSAGE(parse_object(item, (const unsigned char*)input, &error_pointer, &global_hooks), "Failed to parse object.");
+
+    TEST_ASSERT_TRUE_MESSAGE(print_object(item, 0, false, &unformatted_buffer, &global_hooks), "Failed to print unformatted string.");
+    TEST_ASSERT_EQUAL_STRING_MESSAGE(input, printed_unformatted, "Unformatted object is not correct.");
+
+    TEST_ASSERT_TRUE_MESSAGE(print_object(item, 0, true, &formatted_buffer, &global_hooks), "Failed to print formatted string.");
+    TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, printed_formatted, "Formatted ojbect is not correct.");
+
+    reset(item);
+}
+
+static void print_object_should_print_empty_objects(void)
+{
+    assert_print_object("{\n}", "{}");
+}
+
+static void print_object_should_print_objects_with_one_element(void)
+{
+
+    assert_print_object("{\n\t\"one\":\t1\n}", "{\"one\":1}");
+    assert_print_object("{\n\t\"hello\":\t\"world!\"\n}", "{\"hello\":\"world!\"}");
+    assert_print_object("{\n\t\"array\":\t[]\n}", "{\"array\":[]}");
+    assert_print_object("{\n\t\"null\":\tnull\n}", "{\"null\":null}");
+}
+
+static void print_object_should_print_objects_with_multiple_elements(void)
+{
+    assert_print_object("{\n\t\"one\":\t1,\n\t\"two\":\t2,\n\t\"three\":\t3\n}", "{\"one\":1,\"two\":2,\"three\":3}");
+    assert_print_object("{\n\t\"one\":\t1,\n\t\"NULL\":\tnull,\n\t\"TRUE\":\ttrue,\n\t\"FALSE\":\tfalse,\n\t\"array\":\t[],\n\t\"world\":\t\"hello\",\n\t\"object\":\t{\n\t}\n}", "{\"one\":1,\"NULL\":null,\"TRUE\":true,\"FALSE\":false,\"array\":[],\"world\":\"hello\",\"object\":{}}");
+}
+
+int main(void)
+{
+    /* initialize cJSON item */
+    UNITY_BEGIN();
+
+    RUN_TEST(print_object_should_print_empty_objects);
+    RUN_TEST(print_object_should_print_objects_with_one_element);
+    RUN_TEST(print_object_should_print_objects_with_multiple_elements);
+
+    return UNITY_END();
+}

+ 77 - 0
tests/print_string.c

@@ -0,0 +1,77 @@
+/*
+  Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#include "unity/examples/unity_config.h"
+#include "unity/src/unity.h"
+#include "common.h"
+
+static void assert_print_string(const char *expected, const char *input)
+{
+    unsigned char printed[1024];
+    printbuffer buffer;
+    buffer.buffer = printed;
+    buffer.length = sizeof(printed);
+    buffer.offset = 0;
+    buffer.noalloc = true;
+
+    TEST_ASSERT_TRUE_MESSAGE(print_string_ptr((const unsigned char*)input, &buffer, &global_hooks), "Failed to print string.");
+    TEST_ASSERT_EQUAL_STRING_MESSAGE(expected, printed, "The printed string isn't as expected.");
+}
+
+static void print_string_should_print_empty_strings(void)
+{
+    assert_print_string("\"\"", "");
+    assert_print_string("\"\"", NULL);
+}
+
+static void print_string_should_print_ascii(void)
+{
+    char ascii[0x7F];
+    size_t i = 1;
+
+    /* create ascii table */
+    for (i = 1; i < 0x7F; i++)
+    {
+        ascii[i-1] = (char)i;
+    }
+    ascii[0x7F-1] = '\0';
+
+    assert_print_string("\"\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\b\\t\\n\\u000b\\f\\r\\u000e\\u000f\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017\\u0018\\u0019\\u001a\\u001b\\u001c\\u001d\\u001e\\u001f !\\\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\"",
+            ascii);
+}
+
+static void print_string_should_print_utf8(void)
+{
+    assert_print_string("\"ü猫慕\"", "ü猫慕");
+}
+
+int main(void)
+{
+    /* initialize cJSON item */
+    UNITY_BEGIN();
+
+    RUN_TEST(print_string_should_print_empty_strings);
+    RUN_TEST(print_string_should_print_ascii);
+    RUN_TEST(print_string_should_print_utf8);
+
+    return UNITY_END();
+}

+ 102 - 0
tests/print_value.c

@@ -0,0 +1,102 @@
+/*
+  Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
+
+  Permission is hereby granted, free of charge, to any person obtaining a copy
+  of this software and associated documentation files (the "Software"), to deal
+  in the Software without restriction, including without limitation the rights
+  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+  copies of the Software, and to permit persons to whom the Software is
+  furnished to do so, subject to the following conditions:
+
+  The above copyright notice and this permission notice shall be included in
+  all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+  THE SOFTWARE.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "unity/examples/unity_config.h"
+#include "unity/src/unity.h"
+#include "common.h"
+
+static void assert_print_value(const char *input)
+{
+    unsigned char printed[1024];
+    const unsigned char *error_pointer = NULL;
+    cJSON item[1];
+    printbuffer buffer;
+    buffer.buffer = printed;
+    buffer.length = sizeof(printed);
+    buffer.offset = 0;
+    buffer.noalloc = true;
+
+    memset(item, 0, sizeof(item));
+
+    TEST_ASSERT_NOT_NULL_MESSAGE(parse_value(item, (const unsigned char*)input, &error_pointer, &global_hooks), "Failed to parse value.");
+
+    TEST_ASSERT_TRUE_MESSAGE(print_value(item, 0, false, &buffer, &global_hooks), "Failed to print value.");
+    TEST_ASSERT_EQUAL_STRING_MESSAGE(input, buffer.buffer, "Printed value is not as expected.");
+
+    reset(item);
+}
+
+static void print_value_should_print_null(void)
+{
+    assert_print_value("null");
+}
+
+static void print_value_should_print_true(void)
+{
+    assert_print_value("true");
+}
+
+static void print_value_should_print_false(void)
+{
+    assert_print_value("false");
+}
+
+static void print_value_should_print_number(void)
+{
+    assert_print_value("1.5");
+}
+
+static void print_value_should_print_string(void)
+{
+    assert_print_value("\"\"");
+    assert_print_value("\"hello\"");
+}
+
+static void print_value_should_print_array(void)
+{
+    assert_print_value("[]");
+}
+
+static void print_value_should_print_object(void)
+{
+    assert_print_value("{}");
+}
+
+int main(void)
+{
+    /* initialize cJSON item */
+    UNITY_BEGIN();
+
+    RUN_TEST(print_value_should_print_null);
+    RUN_TEST(print_value_should_print_true);
+    RUN_TEST(print_value_should_print_false);
+    RUN_TEST(print_value_should_print_number);
+    RUN_TEST(print_value_should_print_string);
+    RUN_TEST(print_value_should_print_array);
+    RUN_TEST(print_value_should_print_object);
+
+    return UNITY_END();
+}

+ 2 - 2
tests/unity/README.md

@@ -41,13 +41,13 @@ Example:
 
 
     main()
     main()
     {
     {
-        if (TEST_PROTECT() == 0)
+        if (TEST_PROTECT())
         {
         {
             MyTest();
             MyTest();
         }
         }
     }
     }
 
 
-If MyTest calls `TEST_ABORT`, program control will immediately return to `TEST_PROTECT` with a non-zero return value.
+If MyTest calls `TEST_ABORT`, program control will immediately return to `TEST_PROTECT` with a return value of zero.
 
 
 
 
 Unity Assertion Summary
 Unity Assertion Summary

+ 34 - 1
tests/unity/auto/parseOutput.rb

@@ -65,6 +65,17 @@ class ParseOutput
             @arrayList.push "     <testcase classname=\"" + @testSuite + "\" name=\"" + testName + "\"/>"
             @arrayList.push "     <testcase classname=\"" + @testSuite + "\" name=\"" + testName + "\"/>"
         end          
         end          
     end
     end
+    
+# Test was flagged as having passed so format the output.
+# This is using the Unity fixture output and not the original Unity output.
+    def testPassedUnityFixture(array)
+        testSuite = array[0].sub("TEST(", "")
+        testSuite = testSuite.sub(",", "")
+        testName = array[1].sub(")", "")
+        if @xmlOut == true
+            @arrayList.push "     <testcase classname=\"" + testSuite + "\" name=\"" + testName + "\"/>"
+        end          
+    end
 
 
 # Test was flagged as being ingored so format the output
 # Test was flagged as being ingored so format the output
     def testIgnored(array)
     def testIgnored(array)
@@ -73,6 +84,14 @@ class ParseOutput
         reason = array[lastItem].chomp
         reason = array[lastItem].chomp
         testSuiteVerify(array[@className])
         testSuiteVerify(array[@className])
         printf "%-40s IGNORED\n", testName
         printf "%-40s IGNORED\n", testName
+
+        if testName.start_with? "TEST("
+          array2 = testName.split(" ")
+          @testSuite = array2[0].sub("TEST(", "")
+          @testSuite = @testSuite.sub(",", "")
+          testName = array2[1].sub(")", "")
+        end
+
         if @xmlOut == true
         if @xmlOut == true
             @arrayList.push "     <testcase classname=\"" + @testSuite + "\" name=\"" + testName + "\">"
             @arrayList.push "     <testcase classname=\"" + @testSuite + "\" name=\"" + testName + "\">"
             @arrayList.push "            <skipped type=\"TEST IGNORED\"> " + reason + " </skipped>"
             @arrayList.push "            <skipped type=\"TEST IGNORED\"> " + reason + " </skipped>"
@@ -87,6 +106,14 @@ class ParseOutput
         reason = array[lastItem].chomp + " at line: " + array[lastItem - 3]
         reason = array[lastItem].chomp + " at line: " + array[lastItem - 3]
         testSuiteVerify(array[@className])
         testSuiteVerify(array[@className])
         printf "%-40s FAILED\n", testName
         printf "%-40s FAILED\n", testName
+        
+        if testName.start_with? "TEST("
+          array2 = testName.split(" ")
+          @testSuite = array2[0].sub("TEST(", "")
+          @testSuite = @testSuite.sub(",", "")
+          testName = array2[1].sub(")", "")
+        end
+        
         if @xmlOut == true
         if @xmlOut == true
             @arrayList.push "     <testcase classname=\"" + @testSuite + "\" name=\"" + testName + "\">"
             @arrayList.push "     <testcase classname=\"" + @testSuite + "\" name=\"" + testName + "\">"
             @arrayList.push "            <failure type=\"ASSERT FAILED\"> " + reason + " </failure>"
             @arrayList.push "            <failure type=\"ASSERT FAILED\"> " + reason + " </failure>"
@@ -138,7 +165,7 @@ class ParseOutput
             lineSize = lineArray.size
             lineSize = lineArray.size
             # If we were able to split the line then we can look to see if any of our target words
             # If we were able to split the line then we can look to see if any of our target words
             # were found.  Case is important.
             # were found.  Case is important.
-            if lineSize >= 4
+            if ((lineSize >= 4) || (line.start_with? "TEST("))
                 # Determine if this test passed
                 # Determine if this test passed
                 if  line.include? ":PASS"
                 if  line.include? ":PASS"
                     testPassed(lineArray)
                     testPassed(lineArray)
@@ -149,6 +176,12 @@ class ParseOutput
                 elsif line.include? ":IGNORE:"
                 elsif line.include? ":IGNORE:"
                     testIgnored(lineArray)
                     testIgnored(lineArray)
                     testIgnore += 1
                     testIgnore += 1
+                elsif line.start_with? "TEST("
+                  if line.include? " PASS"
+                    lineArray = line.split(" ")
+                    testPassedUnityFixture(lineArray)
+                    testPass += 1
+                  end
                 # If none of the keywords are found there are no more tests for this suite so clear
                 # If none of the keywords are found there are no more tests for this suite so clear
                 # the test flag
                 # the test flag
                 else
                 else

+ 3 - 3
tests/unity/src/unity.c

@@ -669,7 +669,7 @@ void UnityAssertEqualFloatArray(UNITY_PTR_ATTRIBUTE const UNITY_FLOAT* expected,
             UnityTestResultsFailBegin(lineNumber);
             UnityTestResultsFailBegin(lineNumber);
             UnityPrint(UnityStrElement);
             UnityPrint(UnityStrElement);
             UnityPrintNumberUnsigned(num_elements - elements - 1);
             UnityPrintNumberUnsigned(num_elements - elements - 1);
-            UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(*ptr_expected, *ptr_actual);
+            UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT((UNITY_DOUBLE)*ptr_expected, (UNITY_DOUBLE)*ptr_actual);
             UnityAddMsgIfSpecified(msg);
             UnityAddMsgIfSpecified(msg);
             UNITY_FAIL_AND_BAIL;
             UNITY_FAIL_AND_BAIL;
         }
         }
@@ -691,7 +691,7 @@ void UnityAssertFloatsWithin(const UNITY_FLOAT delta,
     if (!UnityFloatsWithin(delta, expected, actual))
     if (!UnityFloatsWithin(delta, expected, actual))
     {
     {
         UnityTestResultsFailBegin(lineNumber);
         UnityTestResultsFailBegin(lineNumber);
-        UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT(expected, actual);
+        UNITY_PRINT_EXPECTED_AND_ACTUAL_FLOAT((UNITY_DOUBLE)expected, (UNITY_DOUBLE)actual);
         UnityAddMsgIfSpecified(msg);
         UnityAddMsgIfSpecified(msg);
         UNITY_FAIL_AND_BAIL;
         UNITY_FAIL_AND_BAIL;
     }
     }
@@ -746,7 +746,7 @@ void UnityAssertFloatSpecial(const UNITY_FLOAT actual,
         UnityPrint(trait_names[trait_index]);
         UnityPrint(trait_names[trait_index]);
         UnityPrint(UnityStrWas);
         UnityPrint(UnityStrWas);
 #ifndef UNITY_EXCLUDE_FLOAT_PRINT
 #ifndef UNITY_EXCLUDE_FLOAT_PRINT
-        UnityPrintFloat(actual);
+        UnityPrintFloat((UNITY_DOUBLE)actual);
 #else
 #else
         if (should_be_trait)
         if (should_be_trait)
             UnityPrint(UnityStrNot);
             UnityPrint(UnityStrNot);

Some files were not shown because too many files changed in this diff