diff --git a/.config b/.config
index 734edb1..f38b1fa 100644
--- a/.config
+++ b/.config
@@ -142,26 +142,26 @@ CONFIG_RT_DFS_ELM_WORD_ACCESS=y
# CONFIG_RT_DFS_ELM_USE_LFN_2 is not set
CONFIG_RT_DFS_ELM_USE_LFN_3=y
CONFIG_RT_DFS_ELM_USE_LFN=3
-CONFIG_RT_DFS_ELM_LFN_UNICODE_0=y
+# CONFIG_RT_DFS_ELM_LFN_UNICODE_0 is not set
# CONFIG_RT_DFS_ELM_LFN_UNICODE_1 is not set
-# CONFIG_RT_DFS_ELM_LFN_UNICODE_2 is not set
+CONFIG_RT_DFS_ELM_LFN_UNICODE_2=y
# CONFIG_RT_DFS_ELM_LFN_UNICODE_3 is not set
-CONFIG_RT_DFS_ELM_LFN_UNICODE=0
+CONFIG_RT_DFS_ELM_LFN_UNICODE=2
CONFIG_RT_DFS_ELM_MAX_LFN=255
CONFIG_RT_DFS_ELM_DRIVES=2
CONFIG_RT_DFS_ELM_MAX_SECTOR_SIZE=512
-# CONFIG_RT_DFS_ELM_USE_ERASE is not set
+CONFIG_RT_DFS_ELM_USE_ERASE=y
CONFIG_RT_DFS_ELM_REENTRANT=y
CONFIG_RT_DFS_ELM_MUTEX_TIMEOUT=3000
-# CONFIG_RT_DFS_ELM_USE_EXFAT is not set
+CONFIG_RT_DFS_ELM_USE_EXFAT=y
# end of elm-chan's FatFs, Generic FAT Filesystem Module
CONFIG_RT_USING_DFS_DEVFS=y
# CONFIG_RT_USING_DFS_ROMFS is not set
# CONFIG_RT_USING_DFS_CROMFS is not set
# CONFIG_RT_USING_DFS_RAMFS is not set
-# CONFIG_RT_USING_DFS_TMPFS is not set
-# CONFIG_RT_USING_DFS_MQUEUE is not set
+CONFIG_RT_USING_DFS_TMPFS=y
+CONFIG_RT_USING_DFS_MQUEUE=y
# end of DFS: device virtual file system
# CONFIG_RT_USING_FAL is not set
@@ -177,7 +177,7 @@ CONFIG_RT_USING_SERIAL=y
CONFIG_RT_USING_SERIAL_V1=y
# CONFIG_RT_USING_SERIAL_V2 is not set
# CONFIG_RT_SERIAL_USING_DMA is not set
-CONFIG_RT_SERIAL_RB_BUFSZ=64
+CONFIG_RT_SERIAL_RB_BUFSZ=512
# CONFIG_RT_USING_CAN is not set
# CONFIG_RT_USING_CPUTIME is not set
# CONFIG_RT_USING_I2C is not set
@@ -191,7 +191,9 @@ CONFIG_RT_SERIAL_RB_BUFSZ=64
# CONFIG_RT_USING_MTD_NOR is not set
# CONFIG_RT_USING_MTD_NAND is not set
# CONFIG_RT_USING_PM is not set
-# CONFIG_RT_USING_RTC is not set
+CONFIG_RT_USING_RTC=y
+# CONFIG_RT_USING_ALARM is not set
+CONFIG_RT_USING_SOFT_RTC=y
CONFIG_RT_USING_SDIO=y
CONFIG_RT_SDIO_STACK_SIZE=512
CONFIG_RT_SDIO_THREAD_PRIORITY=15
@@ -214,7 +216,7 @@ CONFIG_RT_USING_SPI_MSD=y
# CONFIG_RT_USING_HWCRYPTO is not set
# CONFIG_RT_USING_PULSE_ENCODER is not set
# CONFIG_RT_USING_INPUT_CAPTURE is not set
-# CONFIG_RT_USING_DEV_BUS is not set
+CONFIG_RT_USING_DEV_BUS=y
# CONFIG_RT_USING_WIFI is not set
# CONFIG_RT_USING_VIRTIO is not set
CONFIG_RT_USING_PIN=y
@@ -464,7 +466,16 @@ CONFIG_RT_USING_POSIX_FS=y
#
# JSON: JavaScript Object Notation, a lightweight data-interchange format
#
-# CONFIG_PKG_USING_CJSON is not set
+CONFIG_PKG_USING_CJSON=y
+CONFIG_PKG_CJSON_PATH="/packages/language/JSON/cJSON"
+CONFIG_PKG_USING_CJSON_V1717=y
+# CONFIG_PKG_USING_CJSON_V1716 is not set
+# CONFIG_PKG_USING_CJSON_V1715 is not set
+# CONFIG_PKG_USING_CJSON_V1714 is not set
+# CONFIG_PKG_USING_CJSON_V159 is not set
+# CONFIG_PKG_USING_CJSON_V102 is not set
+# CONFIG_PKG_USING_CJSON_LATEST_VERSION is not set
+CONFIG_PKG_CJSON_VER="v1.7.17"
# CONFIG_PKG_USING_LJSON is not set
# CONFIG_PKG_USING_RT_CJSON_TOOLS is not set
# CONFIG_PKG_USING_RAPIDJSON is not set
@@ -640,7 +651,13 @@ CONFIG_RT_USING_POSIX_FS=y
# CONFIG_PKG_USING_FILEX is not set
# CONFIG_PKG_USING_LEVELX is not set
# CONFIG_PKG_USING_FLASHDB is not set
-# CONFIG_PKG_USING_SQLITE is not set
+CONFIG_PKG_USING_SQLITE=y
+CONFIG_PKG_SQLITE_PATH="/packages/system/sqlite"
+CONFIG_PKG_SQLITE_VER="v3.19.3"
+CONFIG_PKG_SQLITE_SQL_MAX_LEN=1024
+CONFIG_PKG_SQLITE_DB_NAME_MAX_LEN=64
+# CONFIG_PKG_SQLITE_DAO_EXAMPLE is not set
+CONFIG_PKG_USING_SQLITE_V3193=y
# CONFIG_PKG_USING_RTI is not set
# CONFIG_PKG_USING_DFS_YAFFS is not set
# CONFIG_PKG_USING_LITTLEFS is not set
diff --git a/.cproject b/.cproject
index 93f4e29..2f86a95 100644
--- a/.cproject
+++ b/.cproject
@@ -75,8 +75,12 @@
+
+
+
+
@@ -187,8 +191,12 @@
+
+
+
+
@@ -211,7 +219,7 @@
-
+
diff --git a/.settings/.rtmenus b/.settings/.rtmenus
index d882761..27d9f46 100644
Binary files a/.settings/.rtmenus and b/.settings/.rtmenus differ
diff --git a/.settings/828F.DAPLink.Debug.rttlaunch b/.settings/828F.DAPLink.Debug.rttlaunch
index 9294675..7fe54dc 100644
--- a/.settings/828F.DAPLink.Debug.rttlaunch
+++ b/.settings/828F.DAPLink.Debug.rttlaunch
@@ -1,5 +1,6 @@
+
@@ -12,7 +13,7 @@
-
+
@@ -28,10 +29,12 @@
+
+
@@ -42,13 +45,14 @@
-
+
+
@@ -58,6 +62,6 @@
-
-
+
+
diff --git a/.settings/language.settings.xml b/.settings/language.settings.xml
index e4498f1..485cf0e 100644
--- a/.settings/language.settings.xml
+++ b/.settings/language.settings.xml
@@ -5,7 +5,7 @@
-
+
diff --git a/applications/DB_SQLite.c b/applications/DB_SQLite.c
new file mode 100644
index 0000000..12d43d2
--- /dev/null
+++ b/applications/DB_SQLite.c
@@ -0,0 +1,463 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2025-10-20 Administrator the first version
+ */
+#include
+#include
+#include
+#include
+#include "DB_SQLite.h"
+#include "sqlite3.h"
+
+#define DB_NAME "/SC828.db"
+int db_HelperInit;
+rt_mq_t db_mq = RT_NULL; //
+
+#define DBG_TAG "db_sql"
+#define DBG_LVL DBG_LOG
+#include
+
+// 消息队列对象与缓冲区(静态分配)
+//static struct rt_mq db_mq;
+//static char mq_pool[DB_QUEUE_SIZE * sizeof(struct db_command)];
+
+// 全局数据库连接与状态
+static sqlite3 *g_db = RT_NULL;
+static rt_bool_t db_enabled = RT_FALSE; // 是否允许数据库操作
+static rt_mutex_t state_mutex = RT_NULL; // 状态访问锁
+
+// 函数前向声明
+static void return_timeout(struct db_command *cmd);
+static void return_disabled(struct db_command *cmd);
+static void handle_db_command(struct db_command *cmd);
+static int callback(void *result_str, int argc, char **argv, char **azColName);
+static const char *sql_upgrade_workorder_steps =
+ "CREATE TABLE WorkorderSteps (WorkOrder VARCHAR,DYELOT VARCHAR,ProgramID VARCHAR,Program VARCHAR,ReDye INT DEFAULT (0),"
+ "Mode VARCHAR,Step INT,StepID VARCHAR,StepName VARCHAR,ParameterName VARCHAR,P1 DOUBLE,P2 DOUBLE,P3 DOUBLE,"
+ "P4 DOUBLE,P5 DOUBLE,P6 INT,P7 INT,P8 INT,P9 INT,P10 INT,Remark VARCHAR,StepTime INT,StepID_S1 VARCHAR,"
+ "StepID_S2 VARCHAR,StepID_S3 VARCHAR,StepName_S1 VARCHAR,StepName_S2 VARCHAR,StepName_S3 VARCHAR,P1_S1 DOUBLE,"
+ "P1_S2 DOUBLE,P1_S3 DOUBLE,P2_S1 DOUBLE,P2_S2 DOUBLE,P2_S3 DOUBLE,P3_S1 DOUBLE,P3_S2 DOUBLE,P3_S3 DOUBLE,"
+ "P4_S1 DOUBLE,P4_S2 DOUBLE,P4_S3 DOUBLE,P5_S1 DOUBLE,P5_S2 DOUBLE,P5_S3 DOUBLE); ";
+
+static const char *sql_upgrade_workorder_set =
+ "CREATE TABLE WorkOrderSet (WorkOrder VARCHAR,ReDye INT DEFAULT (0),PumpSpeed INT,Blower INT,Swing INT,ClothWheel INT,Nozzle INT); ";
+
+static const char *sql_upgrade_workorder =
+ "CREATE TABLE WorkOrder (WorkOrder VARCHAR,Dyelot VARCHAR,ReDye INT DEFAULT (0),ProgramName VARCHAR,StartTime DATETIME,EndTime DATETIME,"
+ "Time TEXT,lock INT,State INT,ProgramID VARCHAR,Machines VARCHAR,color VARCHAR,ColorNumber VARCHAR,Client VARCHAR,ClothWeight VARCHAR,"
+ "ClothSpecies VARCHAR,BathRatio VARCHAR,Total VARCHAR,USER VARCHAR,ColorName VARCHAR,Remark TEXT); ";
+
+static const char *sql_upgrade_run_table =
+ "CREATE TABLE RUN (WorkOrder VARCHAR,DYELOT VARCHAR,ProgramID VARCHAR,Program VARCHAR,ReDye INT DEFAULT (0),"
+ "Mode VARCHAR,Step INT,StepID VARCHAR,StepName VARCHAR,ParameterName VARCHAR,P1 DOUBLE,P2 DOUBLE,P3 DOUBLE,"
+ "P4 DOUBLE,P5 DOUBLE,P6 INT,P7 INT,P8 INT,P9 INT,P10 INT,Remark VARCHAR,StepTime INT,StepID_S1 VARCHAR,"
+ "StepID_S2 VARCHAR,StepID_S3 VARCHAR,StepName_S1 VARCHAR,StepName_S2 VARCHAR,StepName_S3 VARCHAR,P1_S1 DOUBLE,"
+ "P1_S2 DOUBLE,P1_S3 DOUBLE,P2_S1 DOUBLE,P2_S2 DOUBLE,P2_S3 DOUBLE,P3_S1 DOUBLE,P3_S2 DOUBLE,P3_S3 DOUBLE,"
+ "P4_S1 DOUBLE,P4_S2 DOUBLE,P4_S3 DOUBLE,P5_S1 DOUBLE,P5_S2 DOUBLE,P5_S3 DOUBLE); ";
+
+static const char *sql_upgrade_dyelot_table =
+ "CREATE TABLE Dyelot (WorkOrder VARCHAR,Dyelot VARCHAR,ReDye INT,Machine VARCHAR,Step INT,Tank INT,State INT,ProductCode VARCHAR,"
+ "ProductName VARCHAR,ProductType INT,Grams FLOAT,Amount FLOAT,CALL_TIME VARCHAR,DispenseEndTime VARCHAR,Type INT); ";
+
+static const char *sql_upgrade_iolog_table =
+ "CREATE TABLE IOLog (ID varchar,IOName varchar,type varchar,Value DOUBLE DEFAULT (0),DIO BOOLEAN,AIO INTEGER DEFAULT (0),PLC varchar); ";
+
+static const char *sql_upgrade_chart_table =
+ "CREATE TABLE Chart (WorkOrder varchar,DYELOT varchar,ReDye INTEGER,Name varchar,Time varchar,MTT DOUBLE DEFAULT (0),MTL DOUBLE DEFAULT (0),"
+ "STTA DOUBLE DEFAULT (0),STLA DOUBLE DEFAULT (0),STTB DOUBLE DEFAULT (0),STLB DOUBLE DEFAULT (0),STTC DOUBLE DEFAULT (0),"
+ "STLC DOUBLE DEFAULT (0),MTH DOUBLE DEFAULT (0),MST DOUBLE DEFAULT (0),MSL DOUBLE DEFAULT (0),MUT DOUBLE DEFAULT (0)); ";
+
+// ------------------------
+// 内部工具函数
+// ------------------------
+
+/**
+ * @brief 返回超时错误(排队时间过长)
+ */
+static void return_timeout(struct db_command *cmd) {
+ cmd->result_code = -RT_ETIMEOUT;
+ rt_strncpy(cmd->result, "Cmd timeout: waited too long in queue", MAX_RESULT_LEN - 1);
+ if (cmd->reply_sem) {
+ rt_sem_release(cmd->reply_sem);
+ }
+}
+
+/**
+ * @brief 返回“数据库已禁用”错误
+ */
+static void return_disabled(struct db_command *cmd) {
+ cmd->result_code = -RT_ENODEV;
+ rt_strncpy(cmd->result, "Error: Database is disabled", MAX_RESULT_LEN - 1);
+ if (cmd->reply_sem) {
+ rt_sem_release(cmd->reply_sem);
+ }
+}
+
+/**
+ * @brief SELECT 查询回调函数
+ */
+static int callback(void *result_str, int argc, char **argv, char **azColName) {
+ char *str = (char *)result_str;
+ int len = rt_strlen(str);
+ for (int i = 0; i < argc; i++) {
+ rt_snprintf(str + len, MAX_RESULT_LEN - len, "%s=%s|",
+ azColName[i], argv[i] ? argv[i] : "NULL");
+ len = rt_strlen(str);
+ }
+ return 0;
+}
+
+/**
+ * @brief 执行具体 SQL 操作
+ */
+static void handle_db_command(struct db_command *cmd) {
+ char *err_msg = 0;
+ int rc;
+
+ switch (cmd->type) {
+ case DB_CMD_INSERT:
+ case DB_CMD_DELETE:
+ case DB_CMD_UPDATE:
+ case DB_CMD_EXEC:
+ rc = sqlite3_exec(g_db, cmd->sql, 0, 0, &err_msg);
+ if (rc != SQLITE_OK) {
+ cmd->result_code = rc;
+ rt_strncpy(cmd->result, err_msg, MAX_RESULT_LEN - 1);
+ sqlite3_free(err_msg);
+ } else {
+ cmd->result_code = 0;
+ rt_strncpy(cmd->result, "OK", MAX_RESULT_LEN - 1);
+ }
+ break;
+
+ case DB_CMD_SELECT:
+ cmd->result[0] = '\0';
+ rc = sqlite3_exec(g_db, cmd->sql, callback, cmd->result, &err_msg);
+ if (rc != SQLITE_OK) {
+ cmd->result_code = rc;
+ rt_strncpy(cmd->result, err_msg, MAX_RESULT_LEN - 1);
+ sqlite3_free(err_msg);
+ } else {
+ cmd->result_code = 0;
+ if (cmd->result[0] == '\0')
+ rt_strncpy(cmd->result, "No data", MAX_RESULT_LEN - 1);
+ }
+ break;
+
+ case DB_CMD_EXIT:
+ rt_strncpy(cmd->result, "Exit", MAX_RESULT_LEN - 1);
+ cmd->result_code = 0;
+ break;
+
+ default:
+ cmd->result_code = -1;
+ rt_strncpy(cmd->result, "Invalid command", MAX_RESULT_LEN - 1);
+ break;
+ }
+}
+
+// ------------------------
+// 外部接口实现
+// ------------------------
+
+/**
+ * @brief 外部调用:立即关闭数据库并禁用所有操作
+ */
+void close_db_immediately(void) {
+ if (!state_mutex) return;
+
+ rt_mutex_take(state_mutex, RT_WAITING_FOREVER);
+ {
+ if (g_db) {
+ sqlite3_close(g_db);
+ g_db = RT_NULL;
+ }
+ db_enabled = RT_FALSE;
+ }
+ rt_mutex_release(state_mutex);
+ rt_kprintf("Database disabled.\n");
+}
+
+void db_sqlite_init_full(void)
+{
+ LOG_D("[DataBase] Init SQLite");
+ sqlite3_initialize();
+
+ if (access(DB_NAME, F_OK) != 0)
+ {
+ // 先测试文件系统是否可写
+ LOG_E("[SD] Test File System...");
+ int test_fd = open("/db_test.tmp", O_CREAT | O_WRONLY, 0);
+ if (test_fd < 0) {
+ LOG_E("[DB] ERROR: Root filesystem not writable! errno=%d", errno);
+ return;
+ }
+ write(test_fd, "test", 4);
+ close(test_fd);
+ unlink("/db_test.tmp");
+ LOG_D("[SD] File system available.");
+
+ // 创建新数据库
+ if (sqlite3_open(DB_NAME, &g_db) != SQLITE_OK)
+ {
+ if (g_db)
+ {
+ sqlite3_close(g_db);
+ g_db = RT_NULL;
+ }
+ return; //
+ } else{
+ if(sqlite3_exec(g_db,sql_upgrade_workorder_steps, 0, 0, 0)==0)
+ {
+ LOG_D("[DataBASE]WorkorderSteps Created successfully ");
+ }else{
+ LOG_E("[DataBASE]WorkorderSteps Creation failed ");
+ }
+ if(sqlite3_exec(g_db,sql_upgrade_workorder_set, 0, 0, 0)==0)
+ {
+ LOG_D("[DataBASE]WorkOrderSet Created successfully ");
+ }else{
+ LOG_E("[DataBASE]WorkOrderSet Creation failed ");
+ }
+ if(sqlite3_exec(g_db,sql_upgrade_workorder, 0, 0, 0)==0)
+ {
+ LOG_D("[DataBASE]WorkOrder Created successfully ");
+ }else{
+ LOG_E("[DataBASE]WorkOrder Creation failed ");
+ }
+ if(sqlite3_exec(g_db,sql_upgrade_run_table, 0, 0, 0)==0)
+ {
+ LOG_D("[DataBASE]RUN Created successfully ");
+ }else{
+ LOG_E("[DataBASE]RUN Creation failed ");
+ }
+ if(sqlite3_exec(g_db,sql_upgrade_dyelot_table, 0, 0, 0)==0)
+ {
+ LOG_D("[DataBASE]Dyelot Created successfully ");
+ }else{
+ LOG_E("[DataBASE]Dyelot Creation failed \n");
+ }
+ if(sqlite3_exec(g_db,sql_upgrade_iolog_table, 0, 0, 0)==0)
+ {
+ LOG_D("[DataBASE]IOLog Created successfully ");
+ }else{
+ LOG_E("[DataBASE]IOLog Creation failed ");
+ }
+ if(sqlite3_exec(g_db,sql_upgrade_chart_table, 0, 0, 0)==0)
+ {
+ LOG_D("[DataBASE]Chart Created successfully ");
+ }else{
+ LOG_E("[DataBASE]Chart Creation failed ");
+ }
+
+ LOG_D("[DataBase] New database created. Creating tables Name (%s)",DB_NAME);
+ }
+ }else {
+ if (sqlite3_open(DB_NAME, &g_db) == SQLITE_OK)
+ {
+ LOG_D("[DataBase] NAME (%s)",DB_NAME);
+ }
+ }
+}
+
+/**
+ * @brief 数据库处理线程入口函数
+ */
+static void db_sqlite(void *parameter) {
+ struct db_command cmd;
+ rt_tick_t max_wait_ticks = rt_tick_from_millisecond(DB_MAX_WAITING_MS);
+
+ // 检查并初始化数据库文件
+ db_sqlite_init_full();
+
+ //使用 rt_mq_create 动态创建消息队列
+ db_mq = rt_mq_create("db_mq", sizeof(struct db_command), DB_QUEUE_SIZE, RT_IPC_FLAG_FIFO);
+ if (db_mq == RT_NULL) {
+ LOG_E("Failed to create message queue 'db_mq'!");
+ return;
+ }
+
+ // 创建状态锁
+ state_mutex = rt_mutex_create("db_lock", RT_IPC_FLAG_FIFO);
+ if (!state_mutex) {
+ LOG_E("Failed to create db_lock mutex!");
+ rt_mq_delete(db_mq); // 创建失败要清理
+ db_mq = RT_NULL;
+ return;
+ }
+
+ // 默认启用数据库
+ db_enabled = RT_TRUE;
+
+ while (1) {
+ rt_thread_mdelay(500);
+ // 接收命令
+ if (rt_mq_recv(db_mq, &cmd, sizeof(struct db_command), RT_WAITING_FOREVER) != RT_EOK)
+ continue;
+
+ //检查排队超时
+ rt_tick_t now = rt_tick_get();
+ if ((now - cmd.send_tick) > max_wait_ticks) {
+ int wait_ms = (now - cmd.send_tick) * 1000 / RT_TICK_PER_SECOND;
+ rt_kprintf("DROP cmd[type=%d]: waited %dms > limit(%dms)\n", cmd.type, wait_ms, DB_MAX_WAITING_MS);
+ return_timeout(&cmd);
+ continue;
+ }
+
+ //获取当前启用状态
+ rt_bool_t current_enabled;
+ rt_mutex_take(state_mutex, RT_WAITING_FOREVER);
+ current_enabled = db_enabled;
+ rt_mutex_release(state_mutex);
+
+ //若已禁用 → 返回错误
+ if (!current_enabled) {
+ rt_kprintf("Rejected cmd[type=%d]: database is disabled\n", cmd.type);
+ return_disabled(&cmd);
+ if (cmd.type == DB_CMD_EXIT) break;
+ continue;
+ }
+ //打开数据库(如果未打开)
+ rt_mutex_take(state_mutex, RT_WAITING_FOREVER);
+ {
+ if (!g_db && current_enabled) {
+ int rc = sqlite3_open(DB_NAME, &g_db);
+ if (rc != SQLITE_OK) {
+ rt_kprintf("Failed to open DB at %s: %s\n", DB_NAME, sqlite3_errmsg(g_db));
+ sqlite3_close(g_db);
+ g_db = RT_NULL;
+ rt_mutex_release(state_mutex);
+ return_disabled(&cmd);
+ continue;
+ } else {
+ rt_kprintf("Opened database: %s\n", DB_NAME);
+ }
+ }
+ }
+ rt_mutex_release(state_mutex);
+
+ //执行命令
+ handle_db_command(&cmd);
+
+ //返回结果
+ if (cmd.reply_sem) {
+ rt_sem_release(cmd.reply_sem);
+ }
+
+ //退出命令
+ if (cmd.type == DB_CMD_EXIT) break;
+ }
+
+ //清理资源
+ if (g_db) {
+ sqlite3_close(g_db);
+ g_db = RT_NULL;
+ }
+ db_enabled = RT_FALSE;
+
+ if (state_mutex) {
+ rt_mutex_delete(state_mutex);
+ state_mutex = RT_NULL;
+ }
+
+ //删除动态消息队列
+ if (db_mq) {
+ rt_mq_delete(db_mq);
+ db_mq = RT_NULL;
+ }
+
+ LOG_D("Database handler exited.\n");
+}
+
+/**
+ * @brief 向数据库发送命令并等待响应
+ *
+ * @param type 命令类型(INSERT/SELECT等)
+ * @param sql SQL 语句字符串
+ * @param timeout_ms 超时时间(毫秒),0 表示不等待
+ * @return RT_EOK 成功,RT_ETIMEOUT 超时,RT_ERROR 失败
+ */
+rt_err_t db_send_command(enum db_cmd_type type, const char* sql, rt_int32_t timeout_ms)
+{
+ struct db_command cmd;
+ rt_sem_t sem = RT_NULL;
+ rt_err_t result = RT_EOK;
+
+ // 1. 创建临时信号量用于同步
+ sem = rt_sem_create("db_rep", 0, RT_IPC_FLAG_FIFO);
+ if (sem == RT_NULL)
+ {
+ rt_kprintf("Failed to create reply semaphore!\n");
+ return -RT_ENOMEM;
+ }
+
+ // 2. 构造命令
+ rt_memset(&cmd, 0, sizeof(cmd));
+ cmd.type = type;
+ rt_strncpy(cmd.sql, sql, MAX_SQL_LEN - 1);
+ cmd.reply_sem = sem;
+ cmd.send_tick = rt_tick_get();
+
+ // 3. 发送命令到消息队列
+ if (rt_mq_send(db_mq, &cmd, sizeof(cmd)) != RT_EOK)
+ {
+ rt_kprintf("Failed to send command to db_mq!\n");
+ rt_sem_delete(sem);
+ return -RT_ERROR;
+ }
+
+ // 4. 等待数据库线程处理完成(超时保护)
+ if (timeout_ms > 0)
+ {
+ rt_int32_t tick = rt_tick_from_millisecond(timeout_ms);
+ result = rt_sem_take(sem, tick);
+ }
+ else
+ {
+ result = rt_sem_take(sem, RT_WAITING_FOREVER); // 永久等待(慎用)
+ }
+
+ // 5. 检查结果
+ if (result == RT_EOK)
+ {
+ rt_kprintf("SQL executed: code=%d\n", cmd.result_code);
+ if (type == DB_CMD_SELECT || strlen(cmd.result) > 0)
+ {
+ rt_kprintf("Result: %s\n", cmd.result);
+ }
+ }
+ else if (result == -RT_ETIMEOUT)
+ {
+ rt_kprintf("Database response timeout!\n");
+ }
+ else
+ {
+ rt_kprintf("Wait failed: %d\n", result);
+ }
+
+ // 6. 删除信号量
+ rt_sem_delete(sem);
+
+ return result;
+}
+
+/* 线程 */
+void thread_DB_SQLite(void)
+{
+ rt_thread_t tid;
+ tid = rt_thread_create("db_sql", db_sqlite, RT_NULL, 1024 *32, 3, 10);
+
+ if (tid != RT_NULL)
+ {
+ rt_thread_startup(tid);
+ }
+ else
+ {
+ LOG_D("Failed to create led thread!\n");
+ }
+}
diff --git a/applications/DB_SQLite.h b/applications/DB_SQLite.h
new file mode 100644
index 0000000..6551beb
--- /dev/null
+++ b/applications/DB_SQLite.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2025-10-20 Administrator the first version
+ */
+#ifndef APPLICATIONS_DB_DB_SQLITE_H_
+#define APPLICATIONS_DB_SQLITE_SYS_H_
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ========================
+ * 配置参数
+ * ======================== */
+#ifndef MAX_SQL_LEN
+#define MAX_SQL_LEN (1024) /**< SQL 语句最大长度 */
+#endif
+
+#ifndef MAX_RESULT_LEN
+#define MAX_RESULT_LEN (1024) /**< 结果字符串最大长度 */
+#endif
+
+#ifndef DB_MAX_WAITING_MS
+#define DB_MAX_WAITING_MS (5000) /**< 命令最大等待时间(毫秒),默认 5 秒 */
+#endif
+
+#ifndef DB_QUEUE_SIZE
+#define DB_QUEUE_SIZE (8) /**< 消息队列容量(最多缓存 8 条命令) */
+#endif
+
+/* 补充常见错误码(如果 RT-Thread 未定义) */
+#ifndef RT_ENODEV
+#define RT_ENODEV (-5)
+#endif
+
+#ifndef RT_ETIMEOUT
+#define RT_ETIMEOUT (-4)
+#endif
+
+
+/* ========================
+ * 命令类型枚举
+ * ======================== */
+enum db_cmd_type
+{
+ DB_CMD_INSERT, /**< 插入数据 */
+ DB_CMD_DELETE, /**< 删除数据 */
+ DB_CMD_UPDATE, /**< 更新数据 */
+ DB_CMD_SELECT, /**< 查询数据(返回结果字符串) */
+ DB_CMD_EXEC, /**< 执行任意 SQL(无结果集) */
+ DB_CMD_EXIT /**< 退出数据库线程(内部使用) */
+};
+
+/* ========================
+ * 数据库命令结构体
+ * ======================== */
+struct db_command
+{
+ enum db_cmd_type type; /**< 命令类型 */
+ char sql[MAX_SQL_LEN]; /**< SQL 语句内容 */
+ rt_uint32_t result_code; /**< 返回码(SQLite code 或负的 RT-Error) */
+ char result[MAX_RESULT_LEN]; /**< 结果或错误信息 */
+ rt_sem_t reply_sem; /**< 回应信号量(由调用方创建,用于同步) */
+ rt_tick_t send_tick; /**< 发送时刻的系统 tick,用于超时检测 */
+};
+
+/* ========================
+ * 全局变量声明
+ * ======================== */
+/**
+ * 消息队列句柄:用于向数据库线程发送命令
+ * 必须在 .c 文件中定义为:rt_mq_t db_mq = RT_NULL;
+ */
+extern rt_mq_t db_mq;
+
+
+/* ========================
+ * 函数接口声明
+ * ======================== */
+
+/**
+ * @brief 启动数据库处理线程
+ *
+ * 创建一个名为 "data_sqlite" 的线程,初始化 SQLite 数据库,
+ * 并开始监听 db_mq 中的 SQL 请求。
+ *
+ * @note 若已运行,多次调用无效。
+ */
+void thread_DB_SQLite(void);
+
+/**
+ * @brief 立即关闭数据库并禁止后续操作
+ *
+ * 关闭当前数据库连接,并设置禁用标志。
+ * 之后所有新请求将立即失败,返回“数据库已禁用”。
+ *
+ * @note 此函数是线程安全的。
+ */
+void close_db_immediately(void);
+
+/**
+ * @brief 向数据库发送命令并等待响应
+ *
+ * @param type 命令类型(INSERT/SELECT等)
+ * @param sql SQL 语句字符串
+ * @param timeout_ms 超时时间(毫秒),0 表示不等待
+ * @return RT_EOK 成功,RT_ETIMEOUT 超时,RT_ERROR 失败
+ */
+rt_err_t db_send_command(enum db_cmd_type type, const char* sql, rt_int32_t timeout_ms);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* APPLICATIONS_DB_DB_SQLITE_H_ */
diff --git a/applications/SDIO_elmfatfs.c b/applications/SDIO_elmfatfs.c
index 86dfe73..194183a 100644
--- a/applications/SDIO_elmfatfs.c
+++ b/applications/SDIO_elmfatfs.c
@@ -21,7 +21,7 @@ void sd_mount(void *parameter)
{
LOG_I("SD mount thread started, waiting for SD card...");
int retry = 0;
- while (retry<10)
+ while (retry<5)
{
if (rt_device_find("sd") != RT_NULL)
{
diff --git a/applications/main.c b/applications/main.c
index d82c948..75e9c8b 100644
--- a/applications/main.c
+++ b/applications/main.c
@@ -10,6 +10,7 @@
#include
#include "RUN_LED.h"
+#include "DB_SQLite.h"
#define DBG_TAG "main"
#define DBG_LVL DBG_LOG
@@ -20,7 +21,7 @@ extern rt_sem_t mount_sem; // 引用上面SD挂载线程定义的信号量
int main(void)
{
rt_sem_take(mount_sem, rt_tick_from_millisecond(5000)); // 等待挂载完成,最多等待 5 秒
- rt_thread_mdelay(500);
+ thread_DB_SQLite();
thread_RUN_LED();//运行指示灯线程
return RT_EOK;
diff --git a/applications/sdram_port.c b/applications/sdram_port.c
index 1ed2218..2a368f5 100644
--- a/applications/sdram_port.c
+++ b/applications/sdram_port.c
@@ -16,7 +16,7 @@
static SDRAM_HandleTypeDef hsdram1;
static FMC_SDRAM_CommandTypeDef command;
#ifdef RT_USING_MEMHEAP_AS_HEAP
-static struct rt_memheap sdram_heap;
+struct rt_memheap sdram_heap;
#endif
/**
* @brief Perform the SDRAM exernal memory inialization sequence
@@ -159,7 +159,7 @@ static int SDRAM_Init(void)
LOG_D("sdram init success, mapped at 0x%X, size is %d bytes, data width is %d", SDRAM_BANK_ADDR, SDRAM_SIZE, SDRAM_DATA_WIDTH);
#ifdef RT_USING_MEMHEAP_AS_HEAP
/* If RT_USING_MEMHEAP_AS_HEAP is enabled, SDRAM is initialized to the heap */
- rt_memheap_init(&sdram_heap, "sdram", (void *)SDRAM1_START, SDRAM1_SIZE);
+ rt_memheap_init(&sdram_heap, "SDRAM", (void *)SDRAM1_START, SDRAM1_SIZE);
#endif
}
return result;
diff --git a/drivers/board.c b/drivers/board.c
index 5791ec2..4ed5cd1 100644
--- a/drivers/board.c
+++ b/drivers/board.c
@@ -12,7 +12,7 @@
#include
#include
-struct rt_memheap sdram_AXIRAM,sdram_SRAM1,sdram_SRAM2; // memheap 控制块
+struct rt_memheap sram_DTCMRAM,sram_AXIRAM,sram_SRAM1,sram_SRAM2; // memheap 控制块
rt_weak void rt_hw_board_init()
{
@@ -23,9 +23,10 @@ rt_weak void rt_hw_board_init()
rt_system_heap_init((void *) HEAP_BEGIN, (void *) HEAP_END);
#endif
- rt_memheap_init(&sdram_AXIRAM, "AXIRAM", (void *)AXIRAM_START, AXIRAM_SIZE);
- rt_memheap_init(&sdram_SRAM1, "SRAM1", (void *)SRAM1_START, SRAM1_SIZE);
- rt_memheap_init(&sdram_SRAM2, "SRAM2", (void *)SRAM2_START, SRAM2_SIZE);
+ rt_memheap_init(&sram_DTCMRAM, "DTCMRAM", (void *)DTCMRAM_START, DTCMRAM_SIZE);
+ //rt_memheap_init(&sram_AXIRAM, "AXIRAM", (void *)AXIRAM_START, AXIRAM_SIZE);
+ rt_memheap_init(&sram_SRAM1, "SRAM1", (void *)SRAM1_START, SRAM1_SIZE);
+ //rt_memheap_init(&sram_SRAM2, "SRAM2", (void *)SRAM2_START, SRAM2_SIZE);
hw_board_init(BSP_CLOCK_SOURCE, BSP_CLOCK_SOURCE_FREQ_MHZ, BSP_CLOCK_SYSTEM_FREQ_MHZ);
diff --git a/drivers/board.h b/drivers/board.h
index 568ba92..1b30932 100644
--- a/drivers/board.h
+++ b/drivers/board.h
@@ -33,25 +33,29 @@ extern "C"
#define ROM_SIZE (1024 * 1024)
#define ROM_END ((uint32_t)(ROM_START + ROM_SIZE))
-#define RAM_START (0x20000000)
-#define RAM_SIZE (128 * 1024)
+#define RAM_START (0x24000000)
+#define RAM_SIZE (512 * 1024)
#define RAM_END (RAM_START + RAM_SIZE)
-#define AXIRAM_START (0x24000000)
-#define AXIRAM_SIZE (512*1024)
-#define AXIRAM_END (AXIRAM_START + AXIRAM_SIZE)
+//#define AXIRAM_START (0x24000000)
+//#define AXIRAM_SIZE (512*1024)
+//#define AXIRAM_END (AXIRAM_START + AXIRAM_SIZE)
+
+#define DTCMRAM_START (0x20000000)
+#define DTCMRAM_SIZE (128*1024)
+#define DTCMRAM_END (DTCMRAM_START + DTCMRAM_SIZE)
#define SRAM1_START (0x30000000)
-#define SRAM1_SIZE (128*1024)
+#define SRAM1_SIZE (288*1024)
#define SRAM1_END (SRAM1_START + SRAM1_SIZE)
-#define SRAM2_START (0x30020000)
-#define SRAM2_SIZE (128*1024)
-#define SRAM2_END (SRAM2_START + SRAM2_SIZE)
+//#define SRAM2_START (0x30020000)
+//#define SRAM2_SIZE (128*1024)
+//#define SRAM2_END (SRAM2_START + SRAM2_SIZE)
-#define SRAM3_START (0x30040000)
-#define SRAM3_SIZE (32*1024)
-#define SRAM3_END (SRAM3_START + SRAM3_SIZE)
+//#define SRAM3_START (0x30040000)
+//#define SRAM3_SIZE (32*1024)
+//#define SRAM3_END (SRAM3_START + SRAM3_SIZE)
#define BSP_USING_SDRAM
#define SDRAM1_START (0xC0000000)
diff --git a/drivers/drv_common.c b/drivers/drv_common.c
index 69af6c5..3fcbca1 100644
--- a/drivers/drv_common.c
+++ b/drivers/drv_common.c
@@ -17,7 +17,8 @@ static void reboot(uint8_t argc, char **argv)
{
rt_hw_cpu_reset();
}
-FINSH_FUNCTION_EXPORT_ALIAS(reboot, __cmd_reboot, Reboot System);
+//FINSH_FUNCTION_EXPORT_ALIAS(reboot, __cmd_reboot, Reboot System);
+MSH_CMD_EXPORT(reboot, Reboot System);
#endif /* RT_USING_FINSH */
/* SysTick configuration */
diff --git a/drivers/drv_sdio.c b/drivers/drv_sdio.c
index 29dca87..79b4e73 100644
--- a/drivers/drv_sdio.c
+++ b/drivers/drv_sdio.c
@@ -48,7 +48,9 @@ struct rthw_sdio
#ifndef ALIGN
#define ALIGN(SDIO_ALIGN_LEN) __attribute__((aligned(SDIO_ALIGN_LEN)))
//ALIGN(SDIO_ALIGN_LEN)
-static __attribute__((section(".ram_d1"))) rt_uint8_t cache_buf[SDIO_BUFF_SIZE] __attribute__((aligned(32)));
+//static __attribute__((section(".ram_d1"))) rt_uint8_t cache_buf[SDIO_BUFF_SIZE] __attribute__((aligned(32)));
+
+static rt_uint8_t cache_buf[SDIO_BUFF_SIZE] __attribute__((aligned(32)));
//static rt_uint8_t cache_buf[SDIO_BUFF_SIZE];
/**
* @brief This function get order from sdio.
diff --git a/exclude_list.json b/exclude_list.json
new file mode 100644
index 0000000..396b433
--- /dev/null
+++ b/exclude_list.json
@@ -0,0 +1 @@
+[{"config_name":"Debug","exclude_files":["packages/sqlite-v3.19.3/rtthread_vfs.c","packages/sqlite-v3.19.3/dbhelper.c"]}]
\ No newline at end of file
diff --git a/linkscripts/STM32H743IITx/link.lds b/linkscripts/STM32H743IITx/link.lds
index 8ac513c..2bbcdf0 100644
--- a/linkscripts/STM32H743IITx/link.lds
+++ b/linkscripts/STM32H743IITx/link.lds
@@ -6,10 +6,9 @@
MEMORY
{
ROM (rx) : ORIGIN =0x08000000,LENGTH =2048k
-AXIRAM (rw) : ORIGIN =0x24000000,LENGTH =512k
-RAM (rw) : ORIGIN =0x20000000,LENGTH =128k
-SRAM1 (rw) : ORIGIN =0x30000000,LENGTH =128k
-SRAM2 (rw) : ORIGIN =0x30020000,LENGTH =128k
+TDCMRAM (rw) : ORIGIN =0x20000000,LENGTH =128k
+RAM (rw) : ORIGIN =0x24000000,LENGTH =512k
+SRAM (rw) : ORIGIN =0x30000000,LENGTH =288k
SDRAM (rw) : ORIGIN =0xC0000000,LENGTH =32768k
}
ENTRY(Reset_Handler)
@@ -85,6 +84,18 @@ SECTIONS
} > ROM
__exidx_end = .;
+ .tdcmram(NOLOAD):
+ {
+ . = ALIGN(4);
+ _stdcmram = .;
+
+ *(.tdcmram)
+ *(.tdcmram.*)
+
+ . = ALIGN(4);
+ _etdcmram = .;
+ } > TDCMRAM
+
/* .data section which is used for initialized data */
.data : AT (_sidata)
@@ -106,17 +117,7 @@ SECTIONS
. = ALIGN(4);
/* This is used by the startup in order to initialize the .data secion */
_edata = . ;
- } >RAM
-
-.ram_d1(NOLOAD) :
-{
- . = ALIGN(4);
- _sram_dax = .;
- *(.ram_d1)
- *(.ram_d1.*)
- . = ALIGN(4);
- _eram_d1 = .;
-} >AXIRAM
+ } > RAM
.stack :
{
diff --git a/packages/cJSON-v1.7.17/.gitignore b/packages/cJSON-v1.7.17/.gitignore
new file mode 100644
index 0000000..c6127b3
--- /dev/null
+++ b/packages/cJSON-v1.7.17/.gitignore
@@ -0,0 +1,52 @@
+# Prerequisites
+*.d
+
+# Object files
+*.o
+*.ko
+*.obj
+*.elf
+
+# Linker output
+*.ilk
+*.map
+*.exp
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Libraries
+*.lib
+*.a
+*.la
+*.lo
+
+# Shared objects (inc. Windows DLLs)
+*.dll
+*.so
+*.so.*
+*.dylib
+
+# Executables
+*.exe
+*.out
+*.app
+*.i*86
+*.x86_64
+*.hex
+
+# Debug files
+*.dSYM/
+*.su
+*.idb
+*.pdb
+
+# Kernel Module Compile Results
+*.mod*
+*.cmd
+.tmp_versions/
+modules.order
+Module.symvers
+Mkfile.old
+dkms.conf
diff --git a/packages/cJSON-v1.7.17/CHANGELOG.md b/packages/cJSON-v1.7.17/CHANGELOG.md
new file mode 100644
index 0000000..51261ab
--- /dev/null
+++ b/packages/cJSON-v1.7.17/CHANGELOG.md
@@ -0,0 +1,454 @@
+1.7.17 (Dec 26, 2023)
+======
+Fixes:
+------
+* Fix null reference in cJSON_SetValuestring(CVE-2023-50472), see #809
+* Fix null reference in cJSON_InsertItemInArray(CVE-2023-50471), see #809 and #810
+
+1.7.16 (Jul 5, 2023)
+======
+Features:
+------
+* Add an option for ENABLE_CJSON_VERSION_SO in CMakeLists.txt, see #534
+* Add cmake_policy to CMakeLists.txt, see #163
+* Add cJSON_SetBoolValue, see #639
+* Add meson documentation, see #761
+
+Fixes:
+------
+* Fix memory leak in merge_patch, see #611
+* Fix conflicting target names 'uninstall', see #617
+* Bump cmake version to 3.0 and use new version syntax, see #587
+* Print int without decimal places, see #630
+* Fix 'cjson_utils-static' target not exist, see #625
+* Add allocate check for replace_item_in_object, see #675
+* Fix a null pointer crash in cJSON_ReplaceItemViaPointer, see #726
+
+1.7.15 (Aug 25, 2021)
+======
+Fixes:
+------
+* Fix potential core dumped for strrchr, see [#546](https://github.com/DaveGamble/cJSON/pull/546)
+* Fix null pointer crash in cJSON_CreateXxArray, see [#538](https://github.com/DaveGamble/cJSON/pull/538)
+* Fix several null pointer problems on allocation failure, see [#526](https://github.com/DaveGamble/cJSON/pull/526)
+* Fix a possible dereference of null pointer, see [#519](https://github.com/DaveGamble/cJSON/pull/519)
+* Fix windows build failure about defining nan, see [#518](https://github.com/DaveGamble/cJSON/pull/518)
+
+1.7.14 (Sep 3, 2020)
+======
+Fixes:
+------
+* optimize the way to find tail node, see [#503](https://github.com/DaveGamble/cJSON/pull/503)
+* Fix WError error on macosx because NAN is a float. Thanks @sappo, see [#484](https://github.com/DaveGamble/cJSON/pull/484)
+* Fix some bugs in detach and replace. Thanks @miaoerduo, see [#456](https://github.com/DaveGamble/cJSON/pull/456)
+
+1.7.13 (Apr 2, 2020)
+======
+Features:
+---------
+* add new API of cJSON_ParseWithLength without breaking changes. Thanks @caglarivriz, see [#358](https://github.com/DaveGamble/cJSON/pull/358)
+* add new API of cJSON_GetNumberValue. Thanks @Intuition, see[#385](https://github.com/DaveGamble/cJSON/pull/385)
+* add uninstall target function for CMake. See [#402](https://github.com/DaveGamble/cJSON/pull/402)
+* Improve performance of adding item to array. Thanks @xiaomianhehe, see [#430](https://github.com/DaveGamble/cJSON/pull/430), [#448](https://github.com/DaveGamble/cJSON/pull/448)
+* add new API of cJSON_SetValuestring, for changing the valuestring safely. See [#451](https://github.com/DaveGamble/cJSON/pull/451)
+* add return value for cJSON_AddItemTo... and cJSON_ReplaceItem... (check if the operation successful). See [#453](https://github.com/DaveGamble/cJSON/pull/453)
+
+Fixes:
+------
+* Fix clang -Wfloat-equal warning. Thanks @paulmalovanyi, see [#368](https://github.com/DaveGamble/cJSON/pull/368)
+* Fix make failed in mac os. See [#405](https://github.com/DaveGamble/cJSON/pull/405)
+* Fix memory leak in cJSONUtils_FindPointerFromObjectTo. Thanks @andywolk for reporting, see [#414](https://github.com/DaveGamble/cJSON/issues/414)
+* Fix bug in encode_string_as_pointer. Thanks @AIChangJiang for reporting, see [#439](https://github.com/DaveGamble/cJSON/issues/439)
+
+1.7.12 (May 17, 2019)
+======
+Fixes:
+------
+* Fix infinite loop in `cJSON_Minify` (potential Denial of Service). Thanks @Alanscut for reporting, see [#354](https://github.com/DaveGamble/cJSON/issues/354)
+* Fix link error for Visual Studio. Thanks @tan-wei, see [#352](https://github.com/DaveGamble/cJSON/pull/352).
+* Undefine `true` and `false` for `cJSON_Utils` before redefining them. Thanks @raiden00pl, see [#347](https://github.com/DaveGamble/cJSON/pull/347).
+
+1.7.11 (Apr 15, 2019)
+======
+Fixes:
+------
+* Fix a bug where cJSON_Minify could overflow it's buffer, both reading and writing. This is a security issue, see [#338](https://github.com/DaveGamble/cJSON/issues/338). Big thanks @bigric3 for reporting.
+* Unset `true` and `false` macros before setting them if they exist. See [#339](https://github.com/DaveGamble/cJSON/issues/339), thanks @raiden00pl for reporting
+
+1.7.10 (Dec 21, 2018)
+======
+Fixes:
+------
+* Fix package config file for `libcjson`. Thanks @shiluotang for reporting [#321](https://github.com/DaveGamble/cJSON/issues/321)
+* Correctly split lists in `cJSON_Utils`'s merge sort. Thanks @andysCaplin for the fix [#322](https://github.com/DaveGamble/cJSON/issues/322)
+
+1.7.9 (Dec 16, 2018)
+=====
+Fixes:
+------
+* Fix a bug where `cJSON_GetObjectItemCaseSensitive` would pass a nullpointer to `strcmp` when called on an array, see [#315](https://github.com/DaveGamble/cJSON/issues/315). Thanks @yuweol for reporting.
+* Fix error in `cJSON_Utils` where the case sensitivity was not respected, see [#317](https://github.com/DaveGamble/cJSON/pull/317). Thanks @yuta-oxo for fixing.
+* Fix some warnings detected by the Visual Studio Static Analyzer, see [#307](https://github.com/DaveGamble/cJSON/pull/307). Thanks @bnason-nf
+
+1.7.8 (Sep 22, 2018)
+======
+Fixes:
+------
+* cJSON now works with the `__stdcall` calling convention on Windows, see [#295](https://github.com/DaveGamble/cJSON/pull/295), thanks @zhindes for contributing
+
+1.7.7 (May 22, 2018)
+=====
+Fixes:
+------
+* Fix a memory leak when realloc fails, see [#267](https://github.com/DaveGamble/cJSON/issues/267), thanks @AlfieDeng for reporting
+* Fix a typo in the header file, see [#266](https://github.com/DaveGamble/cJSON/pull/266), thanks @zhaozhixu
+
+1.7.6 (Apr 13, 2018)
+=====
+Fixes:
+------
+* Add `SONAME` to the ELF files built by the Makefile, see [#252](https://github.com/DaveGamble/cJSON/issues/252), thanks @YanhaoMo for reporting
+* Add include guards and `extern "C"` to `cJSON_Utils.h`, see [#256](https://github.com/DaveGamble/cJSON/issues/256), thanks @daschfg for reporting
+
+Other changes:
+* Mark the Makefile as deprecated in the README.
+
+1.7.5 (Mar 23, 2018)
+=====
+Fixes:
+------
+* Fix a bug in the JSON Patch implementation of `cJSON Utils`, see [#251](https://github.com/DaveGamble/cJSON/pull/251), thanks @bobkocisko.
+
+1.7.4 (Mar 3, 2018)
+=====
+Fixes:
+------
+* Fix potential use after free if the `string` parameter to `cJSON_AddItemToObject` is an alias of the `string` property of the object that is added,see [#248](https://github.com/DaveGamble/cJSON/issues/248). Thanks @hhallen for reporting.
+
+1.7.3 (Feb 8, 2018)
+=====
+Fixes:
+------
+* Fix potential double free, thanks @projectgus for reporting [#241](https://github.com/DaveGamble/cJSON/issues/241)
+
+1.7.2 (Feb 6, 2018)
+=====
+Fixes:
+------
+* Fix the use of GNUInstallDirs variables and the pkgconfig file. Thanks @zeerd for reporting [#240](https://github.com/DaveGamble/cJSON/pull/240)
+
+1.7.1 (Jan 10, 2018)
+=====
+Fixes:
+------
+* Fixed an Off-By-One error that could lead to an out of bounds write. Thanks @liuyunbin for reporting [#230](https://github.com/DaveGamble/cJSON/issues/230)
+* Fixed two errors with buffered printing. Thanks @liuyunbin for reporting [#230](https://github.com/DaveGamble/cJSON/issues/230)
+
+1.7.0 (Dec 31, 2017)
+=====
+Features:
+---------
+* Large rewrite of the documentation, see [#215](https://github.com/DaveGamble/cJSON/pull/215)
+* Added the `cJSON_GetStringValue` function
+* Added the `cJSON_CreateStringReference` function
+* Added the `cJSON_CreateArrayReference` function
+* Added the `cJSON_CreateObjectReference` function
+* The `cJSON_Add...ToObject` macros are now functions that return a pointer to the added item, see [#226](https://github.com/DaveGamble/cJSON/pull/226)
+
+Fixes:
+------
+* Fix a problem with `GNUInstallDirs` in the CMakeLists.txt, thanks @yangfl, see [#210](https://github.com/DaveGamble/cJSON/pull/210)
+* Fix linking the tests when building as static library, see [#213](https://github.com/DaveGamble/cJSON/issues/213)
+* New overrides for the CMake option `BUILD_SHARED_LIBS`, see [#207](https://github.com/DaveGamble/cJSON/issues/207)
+
+Other Changes:
+--------------
+* Readme: Explain how to include cJSON, see [#211](https://github.com/DaveGamble/cJSON/pull/211)
+* Removed some trailing spaces in the code, thanks @yangfl, see [#212](https://github.com/DaveGamble/cJSON/pull/212)
+* Updated [Unity](https://github.com/ThrowTheSwitch/Unity) and [json-patch-tests](https://github.com/json-patch/json-patch-tests)
+
+1.6.0 (Oct 9, 2017)
+=====
+Features:
+---------
+* You can now build cJSON as both shared and static library at once with CMake using `-DBUILD_SHARED_AND_STATIC_LIBS=On`, see [#178](https://github.com/DaveGamble/cJSON/issues/178)
+* UTF-8 byte order marks are now ignored, see [#184](https://github.com/DaveGamble/cJSON/issues/184)
+* Locales can now be disabled with the option `-DENABLE_LOCALES=Off`, see [#202](https://github.com/DaveGamble/cJSON/issues/202), thanks @Casperinous
+* Better support for MSVC and Visual Studio
+
+Other Changes:
+--------------
+* Add the new warnings `-Wswitch-enum`, `-Wused-but-makred-unused`, `-Wmissing-variable-declarations`, `-Wunused-macro`
+* More number printing tests.
+* Continuous integration testing with AppVeyor (semi automatic at this point), thanks @simon-p-r
+
+1.5.9 (Sep 8, 2017)
+=====
+Fixes:
+------
+* Set the global error pointer even if `return_parse_end` is passed to `cJSON_ParseWithOpts`, see [#200](https://github.com/DaveGamble/cJSON/pull/200), thanks @rmallins
+
+1.5.8 (Aug 21, 2017)
+=====
+Fixes:
+------
+* Fix `make test` in the Makefile, thanks @YanhaoMo for reporting this [#195](https://github.com/DaveGamble/cJSON/issues/195)
+
+1.5.7 (Jul 13, 2017)
+=====
+Fixes:
+------
+* Fix a bug where realloc failing would return a pointer to an invalid memory address. This is a security issue as it could potentially be used by an attacker to write to arbitrary memory addresses, see [#189](https://github.com/DaveGamble/cJSON/issues/189), fixed in [954d61e](https://github.com/DaveGamble/cJSON/commit/954d61e5e7cb9dc6c480fc28ac1cdceca07dd5bd), big thanks @timothyjohncarney for reporting this issue
+* Fix a spelling mistake in the AFL fuzzer dictionary, see [#185](https://github.com/DaveGamble/cJSON/pull/185), thanks @jwilk
+
+1.5.6 (Jun 28, 2017)
+=====
+Fixes:
+------
+* Make cJSON a lot more tolerant about passing NULL pointers to its functions, it should now fail safely instead of dereferencing the pointer, see [#183](https://github.com/DaveGamble/cJSON/pull/183). Thanks @msichal for reporting [#182](https://github.com/DaveGamble/cJSON/issues/182)
+
+1.5.5 (Jun 15, 2017)
+=====
+Fixes:
+------
+* Fix pointers to nested arrays in cJSON_Utils, see [9abe](https://github.com/DaveGamble/cJSON/commit/9abe75e072050f34732a7169740989a082b65134)
+* Fix an error with case sensitivity handling in cJSON_Utils, see [b9cc911](https://github.com/DaveGamble/cJSON/commit/b9cc911831b0b3e1bb72f142389428e59f882b38)
+* Fix cJSON_Compare for arrays that are prefixes of the other and objects that are a subset of the other, see [03ba72f](https://github.com/DaveGamble/cJSON/commit/03ba72faec115160d1f3aea5582d9b6af5d3e473) and [#180](https://github.com/DaveGamble/cJSON/issues/180), thanks @zhengqb for reporting
+
+1.5.4 (Jun 5, 2017)
+======
+Fixes:
+------
+* Fix build with GCC 7.1.1 and optimization level `-O2`, see [bfbd8fe](https://github.com/DaveGamble/cJSON/commit/bfbd8fe0d85f1dd21e508748fc10fc4c27cc51be)
+
+Other Changes:
+--------------
+* Update [Unity](https://github.com/ThrowTheSwitch/Unity) to 3b69beaa58efc41bbbef70a32a46893cae02719d
+
+1.5.3 (May 23, 2017)
+=====
+Fixes:
+------
+* Fix `cJSON_ReplaceItemInObject` not keeping the name of an item, see [#174](https://github.com/DaveGamble/cJSON/issues/174)
+
+1.5.2 (May 10, 2017)
+=====
+Fixes:
+------
+* Fix a reading buffer overflow in `parse_string`, see [a167d9e](https://github.com/DaveGamble/cJSON/commit/a167d9e381e5c84bc03de4e261757b031c0c690d)
+* Fix compiling with -Wcomma, see [186cce3](https://github.com/DaveGamble/cJSON/commit/186cce3ece6ce6dfcb58ac8b2a63f7846c3493ad)
+* Remove leftover attribute from tests, see [b537ca7](https://github.com/DaveGamble/cJSON/commit/b537ca70a35680db66f1f5b8b437f7114daa699a)
+
+1.5.1 (May 6, 2017)
+=====
+Fixes:
+------
+* Add gcc version guard to the Makefile, see [#164](https://github.com/DaveGamble/cJSON/pull/164), thanks @juvasquezg
+* Fix incorrect free in `cJSON_Utils` if custom memory allocator is used, see [#166](https://github.com/DaveGamble/cJSON/pull/166), thanks @prefetchnta
+
+1.5.0 (May 2, 2017)
+=====
+Features:
+* cJSON finally prints numbers without losing precision, see [#153](https://github.com/DaveGamble/cJSON/pull/153), thanks @DeboraG
+* `cJSON_Compare` recursively checks if two cJSON items contain the same values, see [#148](https://github.com/DaveGamble/cJSON/pull/148)
+* Provide case sensitive versions of every function where it matters, see [#158](https://github.com/DaveGamble/cJSON/pull/158) and [#159](https://github.com/DaveGamble/cJSON/pull/159)
+* Added `cJSON_ReplaceItemViaPointer` and `cJSON_DetachItemViaPointer`
+* Added `cJSON_free` and `cJSON_malloc` that expose the internal configured memory allocators. see [02a05ee](https://github.com/DaveGamble/cJSON/commit/02a05eea4e6ba41811f130b322660bea8918e1a0)
+
+
+Enhancements:
+-------------
+* Parse into a buffer, this will allow parsing `\u0000` in the future (not quite yet though)
+* General simplifications and readability improvements
+* More unit tests
+* Update [unity](https://github.com/ThrowTheSwitch/Unity) testing library to 2.4.1
+* Add the [json-patch-tests](https://github.com/json-patch/json-patch-tests) test suite to test cJSON_Utils.
+* Move all tests from `test_utils.c` to unit tests with unity.
+
+Fixes:
+------
+* Fix some warnings with the Microsoft compiler, see [#139](https://github.com/DaveGamble/cJSON/pull/139), thanks @PawelWMS
+* Fix several bugs in cJSON_Utils, mostly found with [json-patch-tests](https://github.com/json-patch/json-patch-tests)
+* Prevent a stack overflow by specifying a maximum nesting depth `CJSON_NESTING_LIMIT`
+
+Other Changes:
+--------------
+* Move generated files in the `library_config` subdirectory.
+
+1.4.7 (Apr 19, 2017)
+=====
+Fixes:
+------
+* Fix `cJSONUtils_ApplyPatches`, it was completely broken and apparently nobody noticed (or at least reported it), see [075a06f](https://github.com/DaveGamble/cJSON/commit/075a06f40bdc4f836c7dd7cad690d253a57cfc50)
+* Fix inconsistent prototype for `cJSON_GetObjectItemCaseSensitive`, see [51d3df6](https://github.com/DaveGamble/cJSON/commit/51d3df6c9f7b56b860c8fb24abe7bab255cd4fa9), thanks @PawelWMS
+
+1.4.6 (Apr 9, 2017)
+=====
+Fixes:
+------
+* Several corrections in the README
+* Making clear that `valueint` should not be written to
+* Fix overflow detection in `ensure`, see [2683d4d](https://github.com/DaveGamble/cJSON/commit/2683d4d9873df87c4bdccc523903ddd78d1ad250)
+* Fix a potential null pointer dereference in cJSON_Utils, see [795c3ac](https://github.com/DaveGamble/cJSON/commit/795c3acabed25c9672006b2c0f40be8845064827)
+* Replace incorrect `sizeof('\0')` with `sizeof("")`, see [84237ff](https://github.com/DaveGamble/cJSON/commit/84237ff48e69825c94261c624eb0376d0c328139)
+* Add caveats section to the README, see [50b3c30](https://github.com/DaveGamble/cJSON/commit/50b3c30dfa89830f8f477ce33713500740ac3b79)
+* Make cJSON locale independent, see [#146](https://github.com/DaveGamble/cJSON/pull/146), Thanks @peterh for reporting
+* Fix compiling without CMake with MSVC, see [#147](https://github.com/DaveGamble/cJSON/pull/147), Thanks @dertuxmalwieder for reporting
+
+1.4.5 (Mar 28, 2017)
+=====
+Fixes:
+------
+* Fix bug in `cJSON_SetNumberHelper`, thanks @mmkeeper, see [#138](https://github.com/DaveGamble/cJSON/issues/138) and [ef34500](https://github.com/DaveGamble/cJSON/commit/ef34500693e8c4a2849d41a4bd66fd19c9ec46c2)
+* Workaround for internal compiler error in GCC 5.4.0 and 6.3.1 on x86 (2f65e80a3471d053fdc3f8aed23d01dd1782a5cb [GCC bugreport](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80097))
+
+1.4.4 (Mar 24, 2017)
+=====
+Fixes:
+------
+* Fix a theoretical integer overflow, (not sure if it is possible on actual hardware), see [e58f7ec](https://github.com/DaveGamble/cJSON/commit/e58f7ec027d00b7cdcbf63e518c1b5268b29b3da)
+* Fix an off by one error, see [cc84a44](https://github.com/DaveGamble/cJSON/commit/cc84a446be20cc283bafdc4d94c050ba1111ac02), thanks @gatzka
+* Double check the offset of the print buffer in `ensure`, see [1934059](https://github.com/DaveGamble/cJSON/commit/1934059554b9a0971e00f79e96900f422cfdd114)
+
+Improvements:
+* Add a note in the header about required buffer size when using `cJSON_PrintPreallocated`, see [4bfb8800](https://github.com/DaveGamble/cJSON/commit/4bfb88009342fb568295a7f6dc4b7fee74fbf022)
+
+1.4.3 (Mar 19, 2017)
+=====
+Fixes:
+------
+* Fix compilation of the tests on 32 bit PowerPC and potentially other systems, see [4ec6e76](https://github.com/DaveGamble/cJSON/commit/4ec6e76ea2eec16f54b58e8c95b4c734e59481e4)
+* Fix compilation with old GCC compilers (4.3+ were tested), see [227d33](https://github.com/DaveGamble/cJSON/commit/227d3398d6b967879761ebe02c1b63dbd6ea6e0d), [466eb8e](https://github.com/DaveGamble/cJSON/commit/466eb8e3f8a65080f2b3ca4a79ab7b72bd539dba), see also [#126](https://github.com/DaveGamble/cJSON/issues/126)
+
+1.4.2 (Mar 16, 2017)
+=====
+Fixes:
+------
+* Fix minimum required cmake version, see [30e1e7a](https://github.com/DaveGamble/cJSON/commit/30e1e7af7c63db9b55f5a3cda977a6c032f0b132)
+* Fix detection of supported compiler flags, see [76e5296](https://github.com/DaveGamble/cJSON/commit/76e5296d0d05ceb3018a9901639e0e171b44a557)
+* Run `cJSON_test` and `cJSON_test_utils` along with unity tests, see [c597601](https://github.com/DaveGamble/cJSON/commit/c597601cf151a757dcf800548f18034d4ddfe2cb)
+
+1.4.1 (Mar 16, 2017)
+=====
+Fixes:
+------
+* Make `print_number` abort with a failure in out of memory situations, see [cf1842](https://github.com/DaveGamble/cJSON/commit/cf1842dc6f64c49451a022308b4415e4d468be0a)
+
+1.4.0 (Mar 4, 2017)
+=====
+Features
+--------
+* Functions to check the type of an item, see [#120](https://github.com/DaveGamble/cJSON/pull/120)
+* Use dllexport on windows and fvisibility on Unix systems for public functions, see [#116](https://github.com/DaveGamble/cJSON/pull/116), thanks @mjerris
+* Remove trailing zeroes from printed numbers, see [#123](https://github.com/DaveGamble/cJSON/pull/123)
+* Expose the internal boolean type `cJSON_bool` in the header, see [2d3520e](https://github.com/DaveGamble/cJSON/commit/2d3520e0b9d0eb870e8886e8a21c571eeddbb310)
+
+Fixes
+* Fix handling of NULL pointers in `cJSON_ArrayForEach`, see [b47d0e3](https://github.com/DaveGamble/cJSON/commit/b47d0e34caaef298edfb7bd09a72cfff21d231ff)
+* Make it compile with GCC 7 (fix -Wimplicit-fallthrough warning), see [9d07917](https://github.com/DaveGamble/cJSON/commit/9d07917feb1b613544a7513d19233d4c851ad7ad)
+
+Other Improvements
+* internally use realloc if available ([#110](https://github.com/DaveGamble/cJSON/pull/110))
+* builtin support for fuzzing with [afl](http://lcamtuf.coredump.cx/afl/) ([#111](https://github.com/DaveGamble/cJSON/pull/111))
+* unit tests for the print functions ([#112](https://github.com/DaveGamble/cJSON/pull/112))
+* Always use buffered printing ([#113](https://github.com/DaveGamble/cJSON/pull/113))
+* simplify the print functions ([#114](https://github.com/DaveGamble/cJSON/pull/114))
+* Add the compiler flags `-Wdouble-conversion`, `-Wparentheses` and `-Wcomma` ([#122](https://github.com/DaveGamble/cJSON/pull/122))
+
+1.3.2 (Mar 1, 2017)
+=====
+Fixes:
+------
+* Don't build the unity library if testing is disabled, see [#121](https://github.com/DaveGamble/cJSON/pull/121). Thanks @ffontaine
+
+1.3.1 (Feb 27, 2017)
+=====
+Fixes:
+------
+* Bugfix release that fixes an out of bounds read, see [#118](https://github.com/DaveGamble/cJSON/pull/118). This shouldn't have any security implications.
+
+1.3.0 (Feb 17, 2017)
+=====
+This release includes a lot of rework in the parser and includes the Cunity unit testing framework, as well as some fixes. I increased the minor version number because there were quite a lot of internal changes.
+
+Features:
+* New type for cJSON structs: `cJSON_Invalid`, see [#108](https://github.com/DaveGamble/cJSON/pull/108)
+
+Fixes:
+------
+* runtime checks for a lot of potential integer overflows
+* fix incorrect return in cJSON_PrintBuffered [cf9d57d](https://github.com/DaveGamble/cJSON/commit/cf9d57d56cac21fc59465b8d26cf29bf6d2a87b3)
+* fix several potential issues found by [Coverity](https://scan.coverity.com/projects/cjson)
+* fix potentially undefined behavior when assigning big numbers to `valueint` ([41e2837](https://github.com/DaveGamble/cJSON/commit/41e2837df1b1091643aff073f2313f6ff3cc10f4))
+ * Numbers exceeding `INT_MAX` or lower than `INT_MIN` will be explicitly assigned to `valueint` as `INT_MAX` and `INT_MIN` respectively (saturation on overflow).
+ * fix the `cJSON_SetNumberValue` macro ([87f7727](https://github.com/DaveGamble/cJSON/commit/87f77274de6b3af00fb9b9a7f3b900ef382296c2)), this slightly changes the behavior, see commit message
+
+Introduce unit tests
+--------------------
+
+* Started writing unit tests with the [Cunity](https://github.com/ThrowTheSwitch/Unity) testing framework. Currently this covers the parser functions.
+
+Also:
+* Support for running the tests with [Valgrind](http://valgrind.org)
+* Support for compiling the tests with [AddressSanitizer](https://github.com/google/sanitizers) and [UndefinedBehaviorSanitizer](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html).
+* `travis.yml` file for running unit tests on travis. (not enabled for the repository yet though [#102](https://github.com/DaveGamble/cJSON/issues/102)
+
+Simplifications
+---------------
+
+After having unit tests for the parser function in place, I started refactoring the parser functions (as well as others) and making them easier to read and maintain.
+* Use `strtod` from the standard library for parsing numbers ([0747669](https://github.com/DaveGamble/cJSON/commit/074766997246481dfc72bfa78f07898a2716473f))
+* Use goto-fail in several parser functions ([#100](https://github.com/DaveGamble/cJSON/pull/100))
+* Rewrite/restructure all of the parsing functions to be easier to understand and have less code paths doing the same as another. ([#109](https://github.com/DaveGamble/cJSON/pull/109))
+* Simplify the buffer allocation strategy to always doubling the needed amount ([9f6fa94](https://github.com/DaveGamble/cJSON/commit/9f6fa94c91a87b71e4c6868dbf2ce431a48517b0))
+* Combined `cJSON_AddItemToObject` and `cJSON_AddItemToObjectCS` to one function ([cf862d](https://github.com/DaveGamble/cJSON/commit/cf862d0fed7f9407e4b046d78d3d8050d2080d12))
+
+Other changes
+-------------
+* Prevent the usage of incompatible C and header versions via preprocessor directive ([123bb1](https://github.com/DaveGamble/cJSON/commit/123bb1af7bfae41d805337fef4b41045ef6c7d25))
+* Let CMake automatically detect compiler flags
+* Add new compiler flags (`-Wundef`, `-Wswitch-default`, `-Wconversion`, `-fstack-protector-strong`) ([#98](https://github.com/DaveGamble/cJSON/pull/98))
+* Change internal sizes from `int` to `size_t` ([ecd5678](https://github.com/DaveGamble/cJSON/commit/ecd5678527a6bc422da694e5be9e9979878fe6a0))
+* Change internal strings from `char*` to `unsigned char*` ([28b9ba4](https://github.com/DaveGamble/cJSON/commit/28b9ba4334e0f7309e867e874a31f395c0ac2474))
+* Add `const` in more places
+
+1.2.1 (Jan 31, 2017)
+=====
+Fixes:
+------
+* Fixes a potential null pointer dereference in cJSON_Utils, discovered using clang's static analyzer by @bnason-nf, see [#96](https://github.com/DaveGamble/cJSON/issues/96)
+
+1.2.0 (Jan 9, 2017)
+=====
+Features:
+---------
+* Add a new type of cJSON item for raw JSON and support printing it. Thanks @loigu, see [#65](https://github.com/DaveGamble/cJSON/pull/65), [#90](https://github.com/DaveGamble/cJSON/pull/90)
+
+Fixes:
+------
+* Compiler warning if const is casted away, Thanks @gatzka, see [#83](https://github.com/DaveGamble/cJSON/pull/83)
+* Fix compile error with strict-overflow on PowerPC, see [#85](https://github.com/DaveGamble/cJSON/issues/85)
+* Fix typo in the README, thanks @MicroJoe, see [#88](https://github.com/DaveGamble/cJSON/pull/88)
+* Add compile flag for compatibility with C++ compilers
+
+1.1.0 (Dec 6, 2016)
+=====
+* Add a function `cJSON_PrintPreallocated` to print to a preallocated buffer, thanks @ChisholmKyle, see [#72](https://github.com/DaveGamble/cJSON/pull/72)
+* More compiler warnings when using Clang or GCC, thanks @gatzka, see [#75](https://github.com/DaveGamble/cJSON/pull/75), [#78](https://github.com/DaveGamble/cJSON/pull/78)
+* fixed a memory leak in `cJSON_Duplicate`, thanks @alperakcan, see [#81](https://github.com/DaveGamble/cJSON/pull/81)
+* fix the `ENABLE_CUSTOM_COMPILER_FLAGS` cmake option
+
+1.0.2 (Nov 25, 2016)
+=====
+* Rename internal boolean type, see [#71](https://github.com/DaveGamble/cJSON/issues/71).
+
+1.0.1 (Nov 20, 2016)
+=====
+Small bugfix release.
+* Fixes a bug with the use of the cJSON structs type in cJSON_Utils, see [d47339e](https://github.com/DaveGamble/cJSON/commit/d47339e2740360e6e0994527d5e4752007480f3a)
+* improve code readability
+* initialize all variables
+
+1.0.0 (Nov 17, 2016)
+=====
+This is the first official versioned release of cJSON. It provides an API version for the shared library and improved Makefile and CMake build files.
diff --git a/packages/cJSON-v1.7.17/CONTRIBUTORS.md b/packages/cJSON-v1.7.17/CONTRIBUTORS.md
new file mode 100644
index 0000000..a9a42c8
--- /dev/null
+++ b/packages/cJSON-v1.7.17/CONTRIBUTORS.md
@@ -0,0 +1,91 @@
+Contributors
+============
+
+Original Author:
+- [Dave Gamble](https://github.com/DaveGamble)
+
+Current Maintainer:
+- [Max Bruckner](https://github.com/FSMaxB)
+- [Alan Wang](https://github.com/Alanscut)
+
+Contributors:
+* [Ajay Bhargav](https://github.com/ajaybhargav)
+* [AlexanderVasiljev](https://github.com/AlexanderVasiljev)
+* [Alper Akcan](https://github.com/alperakcan)
+* [Andrew Tang](https://github.com/singku)
+* [Andy](https://github.com/mlh0101)
+* [Anton Sergeev](https://github.com/anton-sergeev)
+* [Benbuck Nason](https://github.com/bnason-nf)
+* [Bernt Johan Damslora](https://github.com/bjda)
+* [Bob Kocisko](https://github.com/bobkocisko)
+* [Christian Schulze](https://github.com/ChristianSch)
+* [Casperinous](https://github.com/Casperinous)
+* [ChenYuan](https://github.com/zjuchenyuan)
+* [Debora Grosse](https://github.com/DeboraG)
+* [dieyushi](https://github.com/dieyushi)
+* [Dōngwén Huáng (黄东文)](https://github.com/DongwenHuang)
+* [Donough Liu](https://github.com/ldm0)
+* [Erez Oxman](https://github.com/erez-o)
+* Eswar Yaganti
+* [Evan Todd](https://github.com/etodd)
+* [Fabrice Fontaine](https://github.com/ffontaine)
+* Ian Mobley
+* Irwan Djadjadi
+* [hopper-vul](https://github.com/hopper-vul)
+* [HuKeping](https://github.com/HuKeping)
+* [IvanVoid](https://github.com/npi3pak)
+* [Jakub Wilk](https://github.com/jwilk)
+* [Jiri Zouhar](https://github.com/loigu)
+* [Jonathan Fether](https://github.com/jfether)
+* [Joshua Arulsamy](https://github.com/jarulsamy)
+* [Julian Ste](https://github.com/julian-st)
+* [Julián Vásquez](https://github.com/juvasquezg)
+* [Junbo Zheng](https://github.com/Junbo-Zheng)
+* [Kevin Branigan](https://github.com/kbranigan)
+* [Kevin Sapper](https://github.com/sappo)
+* [Kyle Chisholm](https://github.com/ChisholmKyle)
+* [Linus Wallgren](https://github.com/ecksun)
+* [MaxBrandtner](https://github.com/MaxBrandtner)
+* [Mateusz Szafoni](https://github.com/raiden00pl)
+* Mike Pontillo
+* [miaoerduo](https://github.com/miaoerduo)
+* [mohawk2](https://github.com/mohawk2)
+* [Mike Jerris](https://github.com/mjerris)
+* [Mike Robinson](https://github.com/mhrobinson)
+* [Moorthy](https://github.com/moorthy-bs)
+* [myd7349](https://github.com/myd7349)
+* [NancyLi1013](https://github.com/NancyLi1013)
+* Paulo Antonio Alvarez
+* [Paweł Malowany](https://github.com/PawelMalowany)
+* [Pawel Winogrodzki](https://github.com/PawelWMS)
+* [prefetchnta](https://github.com/prefetchnta)
+* [Rafael Leal Dias](https://github.com/rafaeldias)
+* [Randy](https://github.com/randy408)
+* [raiden00pl](https://github.com/raiden00pl)
+* [Robin Mallinson](https://github.com/rmallins)
+* [Rod Vagg](https://github.com/rvagg)
+* [Roland Meertens](https://github.com/rmeertens)
+* [Romain Porte](https://github.com/MicroJoe)
+* [SANJEEV BA](https://github.com/basanjeev)
+* [Sang-Heon Jeon](https://github.com/lntuition)
+* [Sayan Bandyopadhyay](https://github.com/saynb)
+* [Simon Sobisch](https://github.com/GitMensch)
+* [Simon Ricaldone](https://github.com/simon-p-r)
+* [Stoian Ivanov](https://github.com/sdrsdr)
+* [SuperH-0630](https://github.com/SuperH-0630)
+* [Square789](https://github.com/Square789)
+* [Stephan Gatzka](https://github.com/gatzka)
+* [Tony Langhammer](https://github.com/BigBrainAFK)
+* [Vemake](https://github.com/vemakereporter)
+* [Wei Tan](https://github.com/tan-wei)
+* [Weston Schmidt](https://github.com/schmidtw)
+* [xiaomianhehe](https://github.com/xiaomianhehe)
+* [yangfl](https://github.com/yangfl)
+* [yuta-oxo](https://github.com/yuta-oxo)
+* [Zach Hindes](https://github.com/zhindes)
+* [Zhao Zhixu](https://github.com/zhaozhixu)
+* [10km](https://github.com/10km)
+
+And probably more people on [SourceForge](https://sourceforge.net/p/cjson/bugs/search/?q=status%3Aclosed-rejected+or+status%3Aclosed-out-of-date+or+status%3Awont-fix+or+status%3Aclosed-fixed+or+status%3Aclosed&page=0)
+
+Also thanks to all the people who reported bugs and suggested new features.
diff --git a/packages/cJSON-v1.7.17/LICENSE b/packages/cJSON-v1.7.17/LICENSE
new file mode 100644
index 0000000..7a4e2ff
--- /dev/null
+++ b/packages/cJSON-v1.7.17/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 The packages repositories of RT-Thread.
+
+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.
diff --git a/packages/cJSON-v1.7.17/README.md b/packages/cJSON-v1.7.17/README.md
new file mode 100644
index 0000000..0893616
--- /dev/null
+++ b/packages/cJSON-v1.7.17/README.md
@@ -0,0 +1,12 @@
+# cJSON for RT-Thread
+
+[中文页](README_ZH.md) | English
+
+Ultralightweight JSON parser in ANSI C.
+
+Offical repository: https://github.com/DaveGamble/cJSON
+
+Maintenance: [Meco Man](https://github.com/mysterywolf)
+
+Homepage: https://github.com/RT-Thread-packages/cJSON
+
diff --git a/packages/cJSON-v1.7.17/README_ZH.md b/packages/cJSON-v1.7.17/README_ZH.md
new file mode 100644
index 0000000..e40052a
--- /dev/null
+++ b/packages/cJSON-v1.7.17/README_ZH.md
@@ -0,0 +1,12 @@
+# cJSON for RT-Thread
+
+中文页 | [English](README.md)
+
+超轻量级的 C 语言 json 解析库
+
+官方仓库:https://github.com/DaveGamble/cJSON
+
+
+维护:[Meco Man](https://github.com/mysterywolf)
+
+主页:https://github.com/RT-Thread-packages/cJSON
diff --git a/packages/cJSON-v1.7.17/SConscript b/packages/cJSON-v1.7.17/SConscript
new file mode 100644
index 0000000..5d0f564
--- /dev/null
+++ b/packages/cJSON-v1.7.17/SConscript
@@ -0,0 +1,9 @@
+from building import *
+
+cwd = GetCurrentDir()
+src = Glob('*.c')
+CPPPATH = [cwd]
+
+group = DefineGroup('cJSON', src, depend = ['PKG_USING_CJSON'], CPPPATH = CPPPATH)
+
+Return('group')
diff --git a/packages/cJSON-v1.7.17/cJSON.c b/packages/cJSON-v1.7.17/cJSON.c
new file mode 100644
index 0000000..fca9ddb
--- /dev/null
+++ b/packages/cJSON-v1.7.17/cJSON.c
@@ -0,0 +1,3134 @@
+/*
+ Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+/* cJSON */
+/* JSON parser in C. */
+
+/* disable warnings about old C89 functions in MSVC */
+#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER)
+#define _CRT_SECURE_NO_DEPRECATE
+#endif
+
+#ifdef __GNUC__
+#pragma GCC visibility push(default)
+#endif
+#if defined(_MSC_VER)
+#pragma warning (push)
+/* disable warning about single line comments in system headers */
+#pragma warning (disable : 4001)
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifdef ENABLE_LOCALES
+#include
+#endif
+
+#if defined(_MSC_VER)
+#pragma warning (pop)
+#endif
+#ifdef __GNUC__
+#pragma GCC visibility pop
+#endif
+
+#include
+#include "cJSON.h"
+
+/* define our own boolean type */
+#ifdef true
+#undef true
+#endif
+#define true ((cJSON_bool)1)
+
+#ifdef false
+#undef false
+#endif
+#define false ((cJSON_bool)0)
+
+/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */
+#ifndef isinf
+#define isinf(d) (isnan((d - d)) && !isnan(d))
+#endif
+#ifndef isnan
+#define isnan(d) (d != d)
+#endif
+
+#ifndef NAN
+#ifdef _WIN32
+#define NAN sqrt(-1.0)
+#else
+#define NAN 0.0/0.0
+#endif
+#endif
+
+typedef struct {
+ const unsigned char *json;
+ size_t position;
+} error;
+static error global_error = { NULL, 0 };
+
+CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void)
+{
+ return (const char*) (global_error.json + global_error.position);
+}
+
+CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item)
+{
+ if (!cJSON_IsString(item))
+ {
+ return NULL;
+ }
+
+ return item->valuestring;
+}
+
+CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item)
+{
+ if (!cJSON_IsNumber(item))
+ {
+ return (double) NAN;
+ }
+
+ return item->valuedouble;
+}
+
+/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */
+#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 17)
+ #error cJSON.h and cJSON.c have different versions. Make sure that both have the same.
+#endif
+
+CJSON_PUBLIC(const char*) cJSON_Version(void)
+{
+ static char version[15];
+ sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH);
+
+ return version;
+}
+
+/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */
+static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2)
+{
+ if ((string1 == NULL) || (string2 == NULL))
+ {
+ return 1;
+ }
+
+ if (string1 == string2)
+ {
+ return 0;
+ }
+
+ for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++)
+ {
+ if (*string1 == '\0')
+ {
+ return 0;
+ }
+ }
+
+ return tolower(*string1) - tolower(*string2);
+}
+
+typedef struct internal_hooks
+{
+ void *(CJSON_CDECL *allocate)(size_t size);
+ void (CJSON_CDECL *deallocate)(void *pointer);
+ void *(CJSON_CDECL *reallocate)(void *pointer, size_t size);
+} internal_hooks;
+
+#if defined(_MSC_VER)
+/* work around MSVC error C2322: '...' address of dllimport '...' is not static */
+static void * CJSON_CDECL internal_malloc(size_t size)
+{
+ return malloc(size);
+}
+static void CJSON_CDECL internal_free(void *pointer)
+{
+ free(pointer);
+}
+static void * CJSON_CDECL internal_realloc(void *pointer, size_t size)
+{
+ return realloc(pointer, size);
+}
+#elif defined(__RTTHREAD__)
+#define internal_malloc rt_malloc
+#define internal_free rt_free
+#define internal_realloc rt_realloc
+#else
+#define internal_malloc malloc
+#define internal_free free
+#define internal_realloc realloc
+#endif
+
+/* strlen of character literals resolved at compile time */
+#define static_strlen(string_literal) (sizeof(string_literal) - sizeof(""))
+
+static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc };
+
+static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks)
+{
+ size_t length = 0;
+ unsigned char *copy = NULL;
+
+ if (string == NULL)
+ {
+ return NULL;
+ }
+
+ length = strlen((const char*)string) + sizeof("");
+ copy = (unsigned char*)hooks->allocate(length);
+ if (copy == NULL)
+ {
+ return NULL;
+ }
+ rt_memcpy(copy, string, length);
+
+ return copy;
+}
+
+CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks)
+{
+ if (hooks == NULL)
+ {
+ /* Reset hooks */
+ global_hooks.allocate = malloc;
+ global_hooks.deallocate = free;
+ global_hooks.reallocate = realloc;
+ return;
+ }
+
+ global_hooks.allocate = malloc;
+ if (hooks->malloc_fn != NULL)
+ {
+ global_hooks.allocate = hooks->malloc_fn;
+ }
+
+ global_hooks.deallocate = free;
+ if (hooks->free_fn != NULL)
+ {
+ global_hooks.deallocate = hooks->free_fn;
+ }
+
+ /* use realloc only if both free and malloc are used */
+ global_hooks.reallocate = NULL;
+ if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free))
+ {
+ global_hooks.reallocate = realloc;
+ }
+}
+
+/* Internal constructor. */
+static cJSON *cJSON_New_Item(const internal_hooks * const hooks)
+{
+ cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON));
+ if (node)
+ {
+ rt_memset(node, '\0', sizeof(cJSON));
+ }
+
+ return node;
+}
+
+/* Delete a cJSON structure. */
+CJSON_PUBLIC(void) cJSON_Delete(cJSON *item)
+{
+ cJSON *next = NULL;
+ while (item != NULL)
+ {
+ next = item->next;
+ if (!(item->type & cJSON_IsReference) && (item->child != NULL))
+ {
+ cJSON_Delete(item->child);
+ }
+ if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL))
+ {
+ global_hooks.deallocate(item->valuestring);
+ }
+ if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
+ {
+ global_hooks.deallocate(item->string);
+ }
+ global_hooks.deallocate(item);
+ item = next;
+ }
+}
+
+/* get the decimal point character of the current locale */
+static unsigned char get_decimal_point(void)
+{
+#ifdef ENABLE_LOCALES
+ struct lconv *lconv = localeconv();
+ return (unsigned char) lconv->decimal_point[0];
+#else
+ return '.';
+#endif
+}
+
+typedef struct
+{
+ const unsigned char *content;
+ size_t length;
+ size_t offset;
+ size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */
+ internal_hooks hooks;
+} parse_buffer;
+
+/* check if the given size is left to read in a given parse buffer (starting with 1) */
+#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length))
+/* check if the buffer can be accessed at the given index (starting with 0) */
+#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length))
+#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index))
+/* get a pointer to the buffer at the position */
+#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset)
+
+/* Parse the input text to generate a number, and populate the result into item. */
+static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer)
+{
+ double number = 0;
+ unsigned char *after_end = NULL;
+ unsigned char number_c_string[64];
+ unsigned char decimal_point = get_decimal_point();
+ size_t i = 0;
+
+ if ((input_buffer == NULL) || (input_buffer->content == NULL))
+ {
+ return false;
+ }
+
+ /* copy the number into a temporary buffer and replace '.' with the decimal point
+ * of the current locale (for strtod)
+ * This also takes care of '\0' not necessarily being available for marking the end of the input */
+ for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++)
+ {
+ switch (buffer_at_offset(input_buffer)[i])
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case '+':
+ case '-':
+ case 'e':
+ case 'E':
+ number_c_string[i] = buffer_at_offset(input_buffer)[i];
+ break;
+
+ case '.':
+ number_c_string[i] = decimal_point;
+ break;
+
+ default:
+ goto loop_end;
+ }
+ }
+loop_end:
+ number_c_string[i] = '\0';
+
+ number = strtod((const char*)number_c_string, (char**)&after_end);
+ if (number_c_string == after_end)
+ {
+ return false; /* parse_error */
+ }
+
+ item->valuedouble = number;
+
+ /* use saturation in case of overflow */
+ if (number >= INT_MAX)
+ {
+ item->valueint = INT_MAX;
+ }
+ else if (number <= (double)INT_MIN)
+ {
+ item->valueint = INT_MIN;
+ }
+ else
+ {
+ item->valueint = (int)number;
+ }
+
+ item->type = cJSON_Number;
+
+ input_buffer->offset += (size_t)(after_end - number_c_string);
+ return true;
+}
+
+/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */
+CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number)
+{
+ if (number >= INT_MAX)
+ {
+ object->valueint = INT_MAX;
+ }
+ else if (number <= (double)INT_MIN)
+ {
+ object->valueint = INT_MIN;
+ }
+ else
+ {
+ object->valueint = (int)number;
+ }
+
+ return object->valuedouble = number;
+}
+
+CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring)
+{
+ char *copy = NULL;
+ /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */
+ if ((object == NULL) || !(object->type & cJSON_String) || (object->type & cJSON_IsReference))
+ {
+ return NULL;
+ }
+ /* return NULL if the object is corrupted */
+ if (object->valuestring == NULL)
+ {
+ return NULL;
+ }
+ if (strlen(valuestring) <= strlen(object->valuestring))
+ {
+ strcpy(object->valuestring, valuestring);
+ return object->valuestring;
+ }
+ copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks);
+ if (copy == NULL)
+ {
+ return NULL;
+ }
+ if (object->valuestring != NULL)
+ {
+ cJSON_free(object->valuestring);
+ }
+ object->valuestring = copy;
+
+ return copy;
+}
+
+typedef struct
+{
+ unsigned char *buffer;
+ size_t length;
+ size_t offset;
+ size_t depth; /* current nesting depth (for formatted printing) */
+ cJSON_bool noalloc;
+ cJSON_bool format; /* is this print a formatted print */
+ internal_hooks hooks;
+} printbuffer;
+
+/* realloc printbuffer if necessary to have at least "needed" bytes more */
+static unsigned char* ensure(printbuffer * const p, size_t needed)
+{
+ unsigned char *newbuffer = NULL;
+ size_t newsize = 0;
+
+ if ((p == NULL) || (p->buffer == NULL))
+ {
+ return NULL;
+ }
+
+ if ((p->length > 0) && (p->offset >= p->length))
+ {
+ /* make sure that offset is valid */
+ return NULL;
+ }
+
+ if (needed > INT_MAX)
+ {
+ /* sizes bigger than INT_MAX are currently not supported */
+ return NULL;
+ }
+
+ needed += p->offset + 1;
+ if (needed <= p->length)
+ {
+ return p->buffer + p->offset;
+ }
+
+ if (p->noalloc) {
+ return NULL;
+ }
+
+ /* calculate new buffer size */
+ if (needed > (INT_MAX / 2))
+ {
+ /* overflow of int, use INT_MAX if possible */
+ if (needed <= INT_MAX)
+ {
+ newsize = INT_MAX;
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+ else
+ {
+ newsize = needed * 2;
+ }
+
+ if (p->hooks.reallocate != NULL)
+ {
+ /* reallocate with realloc if available */
+ newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize);
+ if (newbuffer == NULL)
+ {
+ p->hooks.deallocate(p->buffer);
+ p->length = 0;
+ p->buffer = NULL;
+
+ return NULL;
+ }
+ }
+ else
+ {
+ /* otherwise reallocate manually */
+ newbuffer = (unsigned char*)p->hooks.allocate(newsize);
+ if (!newbuffer)
+ {
+ p->hooks.deallocate(p->buffer);
+ p->length = 0;
+ p->buffer = NULL;
+
+ return NULL;
+ }
+
+ rt_memcpy(newbuffer, p->buffer, p->offset + 1);
+ p->hooks.deallocate(p->buffer);
+ }
+ p->length = newsize;
+ p->buffer = newbuffer;
+
+ return newbuffer + p->offset;
+}
+
+/* calculate the new length of the string in a printbuffer and update the offset */
+static void update_offset(printbuffer * const buffer)
+{
+ const unsigned char *buffer_pointer = NULL;
+ if ((buffer == NULL) || (buffer->buffer == NULL))
+ {
+ return;
+ }
+ buffer_pointer = buffer->buffer + buffer->offset;
+
+ buffer->offset += strlen((const char*)buffer_pointer);
+}
+
+/* securely comparison of floating-point variables */
+static cJSON_bool compare_double(double a, double b)
+{
+ double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b);
+ return (fabs(a - b) <= maxVal * DBL_EPSILON);
+}
+
+/* Render the number nicely from the given item into a string. */
+static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer)
+{
+ unsigned char *output_pointer = NULL;
+ double d = item->valuedouble;
+ int length = 0;
+ size_t i = 0;
+ unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */
+ unsigned char decimal_point = get_decimal_point();
+ double test = 0.0;
+
+ if (output_buffer == NULL)
+ {
+ return false;
+ }
+
+ /* This checks for NaN and Infinity */
+ if (isnan(d) || isinf(d))
+ {
+ length = sprintf((char*)number_buffer, "null");
+ }
+ else if(d == (double)item->valueint)
+ {
+ length = sprintf((char*)number_buffer, "%d", item->valueint);
+ }
+ else
+ {
+ /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */
+ length = sprintf((char*)number_buffer, "%1.15g", d);
+
+ /* Check whether the original double can be recovered */
+ if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d))
+ {
+ /* If not, print with 17 decimal places of precision */
+ length = sprintf((char*)number_buffer, "%1.17g", d);
+ }
+ }
+
+ /* sprintf failed or buffer overrun occurred */
+ if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1)))
+ {
+ return false;
+ }
+
+ /* reserve appropriate space in the output */
+ output_pointer = ensure(output_buffer, (size_t)length + sizeof(""));
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+
+ /* copy the printed number to the output and replace locale
+ * dependent decimal point with '.' */
+ for (i = 0; i < ((size_t)length); i++)
+ {
+ if (number_buffer[i] == decimal_point)
+ {
+ output_pointer[i] = '.';
+ continue;
+ }
+
+ output_pointer[i] = number_buffer[i];
+ }
+ output_pointer[i] = '\0';
+
+ output_buffer->offset += (size_t)length;
+
+ return true;
+}
+
+/* parse 4 digit hexadecimal number */
+static unsigned parse_hex4(const unsigned char * const input)
+{
+ unsigned int h = 0;
+ size_t i = 0;
+
+ for (i = 0; i < 4; i++)
+ {
+ /* parse digit */
+ if ((input[i] >= '0') && (input[i] <= '9'))
+ {
+ h += (unsigned int) input[i] - '0';
+ }
+ else if ((input[i] >= 'A') && (input[i] <= 'F'))
+ {
+ h += (unsigned int) 10 + input[i] - 'A';
+ }
+ else if ((input[i] >= 'a') && (input[i] <= 'f'))
+ {
+ h += (unsigned int) 10 + input[i] - 'a';
+ }
+ else /* invalid */
+ {
+ return 0;
+ }
+
+ if (i < 3)
+ {
+ /* shift left to make place for the next nibble */
+ h = h << 4;
+ }
+ }
+
+ return h;
+}
+
+/* converts a UTF-16 literal to UTF-8
+ * A literal can be one or two sequences of the form \uXXXX */
+static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer)
+{
+ long unsigned int codepoint = 0;
+ unsigned int first_code = 0;
+ const unsigned char *first_sequence = input_pointer;
+ unsigned char utf8_length = 0;
+ unsigned char utf8_position = 0;
+ unsigned char sequence_length = 0;
+ unsigned char first_byte_mark = 0;
+
+ if ((input_end - first_sequence) < 6)
+ {
+ /* input ends unexpectedly */
+ goto fail;
+ }
+
+ /* get the first utf16 sequence */
+ first_code = parse_hex4(first_sequence + 2);
+
+ /* check that the code is valid */
+ if (((first_code >= 0xDC00) && (first_code <= 0xDFFF)))
+ {
+ goto fail;
+ }
+
+ /* UTF16 surrogate pair */
+ if ((first_code >= 0xD800) && (first_code <= 0xDBFF))
+ {
+ const unsigned char *second_sequence = first_sequence + 6;
+ unsigned int second_code = 0;
+ sequence_length = 12; /* \uXXXX\uXXXX */
+
+ if ((input_end - second_sequence) < 6)
+ {
+ /* input ends unexpectedly */
+ goto fail;
+ }
+
+ if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u'))
+ {
+ /* missing second half of the surrogate pair */
+ goto fail;
+ }
+
+ /* get the second utf16 sequence */
+ second_code = parse_hex4(second_sequence + 2);
+ /* check that the code is valid */
+ if ((second_code < 0xDC00) || (second_code > 0xDFFF))
+ {
+ /* invalid second half of the surrogate pair */
+ goto fail;
+ }
+
+
+ /* calculate the unicode codepoint from the surrogate pair */
+ codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF));
+ }
+ else
+ {
+ sequence_length = 6; /* \uXXXX */
+ codepoint = first_code;
+ }
+
+ /* encode as UTF-8
+ * takes at maximum 4 bytes to encode:
+ * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
+ if (codepoint < 0x80)
+ {
+ /* normal ascii, encoding 0xxxxxxx */
+ utf8_length = 1;
+ }
+ else if (codepoint < 0x800)
+ {
+ /* two bytes, encoding 110xxxxx 10xxxxxx */
+ utf8_length = 2;
+ first_byte_mark = 0xC0; /* 11000000 */
+ }
+ else if (codepoint < 0x10000)
+ {
+ /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */
+ utf8_length = 3;
+ first_byte_mark = 0xE0; /* 11100000 */
+ }
+ else if (codepoint <= 0x10FFFF)
+ {
+ /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */
+ utf8_length = 4;
+ first_byte_mark = 0xF0; /* 11110000 */
+ }
+ else
+ {
+ /* invalid unicode codepoint */
+ goto fail;
+ }
+
+ /* encode as utf8 */
+ for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--)
+ {
+ /* 10xxxxxx */
+ (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF);
+ codepoint >>= 6;
+ }
+ /* encode first byte */
+ if (utf8_length > 1)
+ {
+ (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF);
+ }
+ else
+ {
+ (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F);
+ }
+
+ *output_pointer += utf8_length;
+
+ return sequence_length;
+
+fail:
+ return 0;
+}
+
+/* Parse the input text into an unescaped cinput, and populate item. */
+static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer)
+{
+ const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1;
+ const unsigned char *input_end = buffer_at_offset(input_buffer) + 1;
+ unsigned char *output_pointer = NULL;
+ unsigned char *output = NULL;
+
+ /* not a string */
+ if (buffer_at_offset(input_buffer)[0] != '\"')
+ {
+ goto fail;
+ }
+
+ {
+ /* calculate approximate size of the output (overestimate) */
+ size_t allocation_length = 0;
+ size_t skipped_bytes = 0;
+ while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"'))
+ {
+ /* is escape sequence */
+ if (input_end[0] == '\\')
+ {
+ if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length)
+ {
+ /* prevent buffer overflow when last input character is a backslash */
+ goto fail;
+ }
+ skipped_bytes++;
+ input_end++;
+ }
+ input_end++;
+ }
+ if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"'))
+ {
+ goto fail; /* string ended unexpectedly */
+ }
+
+ /* This is at most how much we need for the output */
+ allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes;
+ output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof(""));
+ if (output == NULL)
+ {
+ goto fail; /* allocation failure */
+ }
+ }
+
+ output_pointer = output;
+ /* loop through the string literal */
+ while (input_pointer < input_end)
+ {
+ if (*input_pointer != '\\')
+ {
+ *output_pointer++ = *input_pointer++;
+ }
+ /* escape sequence */
+ else
+ {
+ unsigned char sequence_length = 2;
+ if ((input_end - input_pointer) < 1)
+ {
+ goto fail;
+ }
+
+ switch (input_pointer[1])
+ {
+ case 'b':
+ *output_pointer++ = '\b';
+ break;
+ case 'f':
+ *output_pointer++ = '\f';
+ break;
+ case 'n':
+ *output_pointer++ = '\n';
+ break;
+ case 'r':
+ *output_pointer++ = '\r';
+ break;
+ case 't':
+ *output_pointer++ = '\t';
+ break;
+ case '\"':
+ case '\\':
+ case '/':
+ *output_pointer++ = input_pointer[1];
+ break;
+
+ /* UTF-16 literal */
+ case 'u':
+ sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer);
+ if (sequence_length == 0)
+ {
+ /* failed to convert UTF16-literal to UTF-8 */
+ goto fail;
+ }
+ break;
+
+ default:
+ goto fail;
+ }
+ input_pointer += sequence_length;
+ }
+ }
+
+ /* zero terminate the output */
+ *output_pointer = '\0';
+
+ item->type = cJSON_String;
+ item->valuestring = (char*)output;
+
+ input_buffer->offset = (size_t) (input_end - input_buffer->content);
+ input_buffer->offset++;
+
+ return true;
+
+fail:
+ if (output != NULL)
+ {
+ input_buffer->hooks.deallocate(output);
+ }
+
+ if (input_pointer != NULL)
+ {
+ input_buffer->offset = (size_t)(input_pointer - input_buffer->content);
+ }
+
+ return false;
+}
+
+/* Render the cstring provided to an escaped version that can be printed. */
+static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer)
+{
+ const unsigned char *input_pointer = NULL;
+ unsigned char *output = NULL;
+ unsigned char *output_pointer = NULL;
+ size_t output_length = 0;
+ /* numbers of additional characters needed for escaping */
+ size_t escape_characters = 0;
+
+ if (output_buffer == NULL)
+ {
+ return false;
+ }
+
+ /* empty string */
+ if (input == NULL)
+ {
+ output = ensure(output_buffer, sizeof("\"\""));
+ if (output == NULL)
+ {
+ return false;
+ }
+ strcpy((char*)output, "\"\"");
+
+ return true;
+ }
+
+ /* set "flag" to 1 if something needs to be escaped */
+ for (input_pointer = input; *input_pointer; input_pointer++)
+ {
+ switch (*input_pointer)
+ {
+ case '\"':
+ case '\\':
+ case '\b':
+ case '\f':
+ case '\n':
+ case '\r':
+ case '\t':
+ /* one character escape sequence */
+ escape_characters++;
+ break;
+ default:
+ if (*input_pointer < 32)
+ {
+ /* UTF-16 escape sequence uXXXX */
+ escape_characters += 5;
+ }
+ break;
+ }
+ }
+ output_length = (size_t)(input_pointer - input) + escape_characters;
+
+ output = ensure(output_buffer, output_length + sizeof("\"\""));
+ if (output == NULL)
+ {
+ return false;
+ }
+
+ /* no characters have to be escaped */
+ if (escape_characters == 0)
+ {
+ output[0] = '\"';
+ rt_memcpy(output + 1, input, output_length);
+ output[output_length + 1] = '\"';
+ output[output_length + 2] = '\0';
+
+ return true;
+ }
+
+ output[0] = '\"';
+ output_pointer = output + 1;
+ /* copy the string */
+ for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++)
+ {
+ if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\'))
+ {
+ /* normal character, copy */
+ *output_pointer = *input_pointer;
+ }
+ else
+ {
+ /* character needs to be escaped */
+ *output_pointer++ = '\\';
+ switch (*input_pointer)
+ {
+ case '\\':
+ *output_pointer = '\\';
+ break;
+ case '\"':
+ *output_pointer = '\"';
+ break;
+ case '\b':
+ *output_pointer = 'b';
+ break;
+ case '\f':
+ *output_pointer = 'f';
+ break;
+ case '\n':
+ *output_pointer = 'n';
+ break;
+ case '\r':
+ *output_pointer = 'r';
+ break;
+ case '\t':
+ *output_pointer = 't';
+ break;
+ default:
+ /* escape and print as unicode codepoint */
+ sprintf((char*)output_pointer, "u%04x", *input_pointer);
+ output_pointer += 4;
+ break;
+ }
+ }
+ }
+ output[output_length + 1] = '\"';
+ output[output_length + 2] = '\0';
+
+ return true;
+}
+
+/* Invoke print_string_ptr (which is useful) on an item. */
+static cJSON_bool print_string(const cJSON * const item, printbuffer * const p)
+{
+ return print_string_ptr((unsigned char*)item->valuestring, p);
+}
+
+/* Predeclare these prototypes. */
+static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer);
+static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer);
+static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer);
+static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer);
+static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer);
+static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer);
+
+/* Utility to jump whitespace and cr/lf */
+static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer)
+{
+ if ((buffer == NULL) || (buffer->content == NULL))
+ {
+ return NULL;
+ }
+
+ if (cannot_access_at_index(buffer, 0))
+ {
+ return buffer;
+ }
+
+ while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32))
+ {
+ buffer->offset++;
+ }
+
+ if (buffer->offset == buffer->length)
+ {
+ buffer->offset--;
+ }
+
+ return buffer;
+}
+
+/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */
+static parse_buffer *skip_utf8_bom(parse_buffer * const buffer)
+{
+ if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0))
+ {
+ return NULL;
+ }
+
+ if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0))
+ {
+ buffer->offset += 3;
+ }
+
+ return buffer;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated)
+{
+ size_t buffer_length;
+
+ if (NULL == value)
+ {
+ return NULL;
+ }
+
+ /* Adding null character size due to require_null_terminated. */
+ buffer_length = strlen(value) + sizeof("");
+
+ return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated);
+}
+
+/* Parse an object - create a new root, and populate. */
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated)
+{
+ parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } };
+ cJSON *item = NULL;
+
+ /* reset error position */
+ global_error.json = NULL;
+ global_error.position = 0;
+
+ if (value == NULL || 0 == buffer_length)
+ {
+ goto fail;
+ }
+
+ buffer.content = (const unsigned char*)value;
+ buffer.length = buffer_length;
+ buffer.offset = 0;
+ buffer.hooks = global_hooks;
+
+ item = cJSON_New_Item(&global_hooks);
+ if (item == NULL) /* memory fail */
+ {
+ goto fail;
+ }
+
+ if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer))))
+ {
+ /* parse failure. ep is set. */
+ goto fail;
+ }
+
+ /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
+ if (require_null_terminated)
+ {
+ buffer_skip_whitespace(&buffer);
+ if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0')
+ {
+ goto fail;
+ }
+ }
+ if (return_parse_end)
+ {
+ *return_parse_end = (const char*)buffer_at_offset(&buffer);
+ }
+
+ return item;
+
+fail:
+ if (item != NULL)
+ {
+ cJSON_Delete(item);
+ }
+
+ if (value != NULL)
+ {
+ error local_error;
+ local_error.json = (const unsigned char*)value;
+ local_error.position = 0;
+
+ if (buffer.offset < buffer.length)
+ {
+ local_error.position = buffer.offset;
+ }
+ else if (buffer.length > 0)
+ {
+ local_error.position = buffer.length - 1;
+ }
+
+ if (return_parse_end != NULL)
+ {
+ *return_parse_end = (const char*)local_error.json + local_error.position;
+ }
+
+ global_error = local_error;
+ }
+
+ return NULL;
+}
+
+/* Default options for cJSON_Parse */
+CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value)
+{
+ return cJSON_ParseWithOpts(value, 0, 0);
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length)
+{
+ return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0);
+}
+
+#define cjson_min(a, b) (((a) < (b)) ? (a) : (b))
+
+static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks)
+{
+ static const size_t default_buffer_size = 256;
+ printbuffer buffer[1];
+ unsigned char *printed = NULL;
+
+ rt_memset(buffer, 0, sizeof(buffer));
+
+ /* create buffer */
+ buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size);
+ buffer->length = default_buffer_size;
+ buffer->format = format;
+ buffer->hooks = *hooks;
+ if (buffer->buffer == NULL)
+ {
+ goto fail;
+ }
+
+ /* print the value */
+ if (!print_value(item, buffer))
+ {
+ goto fail;
+ }
+ update_offset(buffer);
+
+ /* check if reallocate is available */
+ if (hooks->reallocate != NULL)
+ {
+ printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1);
+ if (printed == NULL) {
+ goto fail;
+ }
+ buffer->buffer = NULL;
+ }
+ else /* otherwise copy the JSON over to a new buffer */
+ {
+ printed = (unsigned char*) hooks->allocate(buffer->offset + 1);
+ if (printed == NULL)
+ {
+ goto fail;
+ }
+ rt_memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1));
+ printed[buffer->offset] = '\0'; /* just to be sure */
+
+ /* free the buffer */
+ hooks->deallocate(buffer->buffer);
+ }
+
+ return printed;
+
+fail:
+ if (buffer->buffer != NULL)
+ {
+ hooks->deallocate(buffer->buffer);
+ }
+
+ if (printed != NULL)
+ {
+ hooks->deallocate(printed);
+ }
+
+ return NULL;
+}
+
+/* Render a cJSON item/entity/structure to text. */
+CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item)
+{
+ return (char*)print(item, true, &global_hooks);
+}
+
+CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item)
+{
+ return (char*)print(item, false, &global_hooks);
+}
+
+CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt)
+{
+ printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
+
+ if (prebuffer < 0)
+ {
+ return NULL;
+ }
+
+ p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer);
+ if (!p.buffer)
+ {
+ return NULL;
+ }
+
+ p.length = (size_t)prebuffer;
+ p.offset = 0;
+ p.noalloc = false;
+ p.format = fmt;
+ p.hooks = global_hooks;
+
+ if (!print_value(item, &p))
+ {
+ global_hooks.deallocate(p.buffer);
+ return NULL;
+ }
+
+ return (char*)p.buffer;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format)
+{
+ printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } };
+
+ if ((length < 0) || (buffer == NULL))
+ {
+ return false;
+ }
+
+ p.buffer = (unsigned char*)buffer;
+ p.length = (size_t)length;
+ p.offset = 0;
+ p.noalloc = true;
+ p.format = format;
+ p.hooks = global_hooks;
+
+ return print_value(item, &p);
+}
+
+/* Parser core - when encountering text, process appropriately. */
+static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer)
+{
+ if ((input_buffer == NULL) || (input_buffer->content == NULL))
+ {
+ return false; /* no input */
+ }
+
+ /* parse the different types of values */
+ /* null */
+ if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0))
+ {
+ item->type = cJSON_NULL;
+ input_buffer->offset += 4;
+ return true;
+ }
+ /* false */
+ if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0))
+ {
+ item->type = cJSON_False;
+ input_buffer->offset += 5;
+ return true;
+ }
+ /* true */
+ if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0))
+ {
+ item->type = cJSON_True;
+ item->valueint = 1;
+ input_buffer->offset += 4;
+ return true;
+ }
+ /* string */
+ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"'))
+ {
+ return parse_string(item, input_buffer);
+ }
+ /* number */
+ if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9'))))
+ {
+ return parse_number(item, input_buffer);
+ }
+ /* array */
+ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '['))
+ {
+ return parse_array(item, input_buffer);
+ }
+ /* object */
+ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{'))
+ {
+ return parse_object(item, input_buffer);
+ }
+
+ return false;
+}
+
+/* Render a value to text. */
+static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer)
+{
+ unsigned char *output = NULL;
+
+ if ((item == NULL) || (output_buffer == NULL))
+ {
+ return false;
+ }
+
+ switch ((item->type) & 0xFF)
+ {
+ case cJSON_NULL:
+ output = ensure(output_buffer, 5);
+ if (output == NULL)
+ {
+ return false;
+ }
+ strcpy((char*)output, "null");
+ return true;
+
+ case cJSON_False:
+ output = ensure(output_buffer, 6);
+ if (output == NULL)
+ {
+ return false;
+ }
+ strcpy((char*)output, "false");
+ return true;
+
+ case cJSON_True:
+ output = ensure(output_buffer, 5);
+ if (output == NULL)
+ {
+ return false;
+ }
+ strcpy((char*)output, "true");
+ return true;
+
+ case cJSON_Number:
+ return print_number(item, output_buffer);
+
+ case cJSON_Raw:
+ {
+ size_t raw_length = 0;
+ if (item->valuestring == NULL)
+ {
+ return false;
+ }
+
+ raw_length = strlen(item->valuestring) + sizeof("");
+ output = ensure(output_buffer, raw_length);
+ if (output == NULL)
+ {
+ return false;
+ }
+ rt_memcpy(output, item->valuestring, raw_length);
+ return true;
+ }
+
+ case cJSON_String:
+ return print_string(item, output_buffer);
+
+ case cJSON_Array:
+ return print_array(item, output_buffer);
+
+ case cJSON_Object:
+ return print_object(item, output_buffer);
+
+ default:
+ return false;
+ }
+}
+
+/* Build an array from input text. */
+static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer)
+{
+ cJSON *head = NULL; /* head of the linked list */
+ cJSON *current_item = NULL;
+
+ if (input_buffer->depth >= CJSON_NESTING_LIMIT)
+ {
+ return false; /* to deeply nested */
+ }
+ input_buffer->depth++;
+
+ if (buffer_at_offset(input_buffer)[0] != '[')
+ {
+ /* not an array */
+ goto fail;
+ }
+
+ input_buffer->offset++;
+ buffer_skip_whitespace(input_buffer);
+ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']'))
+ {
+ /* empty array */
+ goto success;
+ }
+
+ /* check if we skipped to the end of the buffer */
+ if (cannot_access_at_index(input_buffer, 0))
+ {
+ input_buffer->offset--;
+ goto fail;
+ }
+
+ /* step back to character in front of the first element */
+ input_buffer->offset--;
+ /* loop through the comma separated array elements */
+ do
+ {
+ /* allocate next item */
+ cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
+ if (new_item == NULL)
+ {
+ goto fail; /* allocation failure */
+ }
+
+ /* attach next item to list */
+ if (head == NULL)
+ {
+ /* start the linked list */
+ current_item = head = new_item;
+ }
+ else
+ {
+ /* add to the end and advance */
+ current_item->next = new_item;
+ new_item->prev = current_item;
+ current_item = new_item;
+ }
+
+ /* parse next value */
+ input_buffer->offset++;
+ buffer_skip_whitespace(input_buffer);
+ if (!parse_value(current_item, input_buffer))
+ {
+ goto fail; /* failed to parse value */
+ }
+ buffer_skip_whitespace(input_buffer);
+ }
+ while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
+
+ if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']')
+ {
+ goto fail; /* expected end of array */
+ }
+
+success:
+ input_buffer->depth--;
+
+ if (head != NULL) {
+ head->prev = current_item;
+ }
+
+ item->type = cJSON_Array;
+ item->child = head;
+
+ input_buffer->offset++;
+
+ return true;
+
+fail:
+ if (head != NULL)
+ {
+ cJSON_Delete(head);
+ }
+
+ return false;
+}
+
+/* Render an array to text */
+static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer)
+{
+ unsigned char *output_pointer = NULL;
+ size_t length = 0;
+ cJSON *current_element = item->child;
+
+ if (output_buffer == NULL)
+ {
+ return false;
+ }
+
+ /* Compose the output array. */
+ /* opening square bracket */
+ output_pointer = ensure(output_buffer, 1);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+
+ *output_pointer = '[';
+ output_buffer->offset++;
+ output_buffer->depth++;
+
+ while (current_element != NULL)
+ {
+ if (!print_value(current_element, output_buffer))
+ {
+ return false;
+ }
+ update_offset(output_buffer);
+ if (current_element->next)
+ {
+ length = (size_t) (output_buffer->format ? 2 : 1);
+ output_pointer = ensure(output_buffer, length + 1);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+ *output_pointer++ = ',';
+ if(output_buffer->format)
+ {
+ *output_pointer++ = ' ';
+ }
+ *output_pointer = '\0';
+ output_buffer->offset += length;
+ }
+ current_element = current_element->next;
+ }
+
+ output_pointer = ensure(output_buffer, 2);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+ *output_pointer++ = ']';
+ *output_pointer = '\0';
+ output_buffer->depth--;
+
+ return true;
+}
+
+/* Build an object from the text. */
+static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer)
+{
+ cJSON *head = NULL; /* linked list head */
+ cJSON *current_item = NULL;
+
+ if (input_buffer->depth >= CJSON_NESTING_LIMIT)
+ {
+ return false; /* to deeply nested */
+ }
+ input_buffer->depth++;
+
+ if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{'))
+ {
+ goto fail; /* not an object */
+ }
+
+ input_buffer->offset++;
+ buffer_skip_whitespace(input_buffer);
+ if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}'))
+ {
+ goto success; /* empty object */
+ }
+
+ /* check if we skipped to the end of the buffer */
+ if (cannot_access_at_index(input_buffer, 0))
+ {
+ input_buffer->offset--;
+ goto fail;
+ }
+
+ /* step back to character in front of the first element */
+ input_buffer->offset--;
+ /* loop through the comma separated array elements */
+ do
+ {
+ /* allocate next item */
+ cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks));
+ if (new_item == NULL)
+ {
+ goto fail; /* allocation failure */
+ }
+
+ /* attach next item to list */
+ if (head == NULL)
+ {
+ /* start the linked list */
+ current_item = head = new_item;
+ }
+ else
+ {
+ /* add to the end and advance */
+ current_item->next = new_item;
+ new_item->prev = current_item;
+ current_item = new_item;
+ }
+
+ /* parse the name of the child */
+ input_buffer->offset++;
+ buffer_skip_whitespace(input_buffer);
+ if (!parse_string(current_item, input_buffer))
+ {
+ goto fail; /* failed to parse name */
+ }
+ buffer_skip_whitespace(input_buffer);
+
+ /* swap valuestring and string, because we parsed the name */
+ current_item->string = current_item->valuestring;
+ current_item->valuestring = NULL;
+
+ if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':'))
+ {
+ goto fail; /* invalid object */
+ }
+
+ /* parse the value */
+ input_buffer->offset++;
+ buffer_skip_whitespace(input_buffer);
+ if (!parse_value(current_item, input_buffer))
+ {
+ goto fail; /* failed to parse value */
+ }
+ buffer_skip_whitespace(input_buffer);
+ }
+ while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ','));
+
+ if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}'))
+ {
+ goto fail; /* expected end of object */
+ }
+
+success:
+ input_buffer->depth--;
+
+ if (head != NULL) {
+ head->prev = current_item;
+ }
+
+ item->type = cJSON_Object;
+ item->child = head;
+
+ input_buffer->offset++;
+ return true;
+
+fail:
+ if (head != NULL)
+ {
+ cJSON_Delete(head);
+ }
+
+ return false;
+}
+
+/* Render an object to text. */
+static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer)
+{
+ unsigned char *output_pointer = NULL;
+ size_t length = 0;
+ cJSON *current_item = item->child;
+
+ if (output_buffer == NULL)
+ {
+ return false;
+ }
+
+ /* Compose the output: */
+ length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */
+ output_pointer = ensure(output_buffer, length + 1);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+
+ *output_pointer++ = '{';
+ output_buffer->depth++;
+ if (output_buffer->format)
+ {
+ *output_pointer++ = '\n';
+ }
+ output_buffer->offset += length;
+
+ while (current_item)
+ {
+ if (output_buffer->format)
+ {
+ size_t i;
+ output_pointer = ensure(output_buffer, output_buffer->depth);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+ for (i = 0; i < output_buffer->depth; i++)
+ {
+ *output_pointer++ = '\t';
+ }
+ output_buffer->offset += output_buffer->depth;
+ }
+
+ /* print key */
+ if (!print_string_ptr((unsigned char*)current_item->string, output_buffer))
+ {
+ return false;
+ }
+ update_offset(output_buffer);
+
+ length = (size_t) (output_buffer->format ? 2 : 1);
+ output_pointer = ensure(output_buffer, length);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+ *output_pointer++ = ':';
+ if (output_buffer->format)
+ {
+ *output_pointer++ = '\t';
+ }
+ output_buffer->offset += length;
+
+ /* print value */
+ if (!print_value(current_item, output_buffer))
+ {
+ return false;
+ }
+ update_offset(output_buffer);
+
+ /* print comma if not last */
+ length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0));
+ output_pointer = ensure(output_buffer, length + 1);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+ if (current_item->next)
+ {
+ *output_pointer++ = ',';
+ }
+
+ if (output_buffer->format)
+ {
+ *output_pointer++ = '\n';
+ }
+ *output_pointer = '\0';
+ output_buffer->offset += length;
+
+ current_item = current_item->next;
+ }
+
+ output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2);
+ if (output_pointer == NULL)
+ {
+ return false;
+ }
+ if (output_buffer->format)
+ {
+ size_t i;
+ for (i = 0; i < (output_buffer->depth - 1); i++)
+ {
+ *output_pointer++ = '\t';
+ }
+ }
+ *output_pointer++ = '}';
+ *output_pointer = '\0';
+ output_buffer->depth--;
+
+ return true;
+}
+
+/* Get Array size/item / object item. */
+CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array)
+{
+ cJSON *child = NULL;
+ size_t size = 0;
+
+ if (array == NULL)
+ {
+ return 0;
+ }
+
+ child = array->child;
+
+ while(child != NULL)
+ {
+ size++;
+ child = child->next;
+ }
+
+ /* FIXME: Can overflow here. Cannot be fixed without breaking the API */
+
+ return (int)size;
+}
+
+static cJSON* get_array_item(const cJSON *array, size_t index)
+{
+ cJSON *current_child = NULL;
+
+ if (array == NULL)
+ {
+ return NULL;
+ }
+
+ current_child = array->child;
+ while ((current_child != NULL) && (index > 0))
+ {
+ index--;
+ current_child = current_child->next;
+ }
+
+ return current_child;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index)
+{
+ if (index < 0)
+ {
+ return NULL;
+ }
+
+ return get_array_item(array, (size_t)index);
+}
+
+static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive)
+{
+ cJSON *current_element = NULL;
+
+ if ((object == NULL) || (name == NULL))
+ {
+ return NULL;
+ }
+
+ current_element = object->child;
+ if (case_sensitive)
+ {
+ while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0))
+ {
+ current_element = current_element->next;
+ }
+ }
+ else
+ {
+ while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0))
+ {
+ current_element = current_element->next;
+ }
+ }
+
+ if ((current_element == NULL) || (current_element->string == NULL)) {
+ return NULL;
+ }
+
+ return current_element;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string)
+{
+ return get_object_item(object, string, false);
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string)
+{
+ return get_object_item(object, string, true);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string)
+{
+ return cJSON_GetObjectItem(object, string) ? 1 : 0;
+}
+
+/* Utility for array list handling. */
+static void suffix_object(cJSON *prev, cJSON *item)
+{
+ prev->next = item;
+ item->prev = prev;
+}
+
+/* Utility for handling references. */
+static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks)
+{
+ cJSON *reference = NULL;
+ if (item == NULL)
+ {
+ return NULL;
+ }
+
+ reference = cJSON_New_Item(hooks);
+ if (reference == NULL)
+ {
+ return NULL;
+ }
+
+ rt_memcpy(reference, item, sizeof(cJSON));
+ reference->string = NULL;
+ reference->type |= cJSON_IsReference;
+ reference->next = reference->prev = NULL;
+ return reference;
+}
+
+static cJSON_bool add_item_to_array(cJSON *array, cJSON *item)
+{
+ cJSON *child = NULL;
+
+ if ((item == NULL) || (array == NULL) || (array == item))
+ {
+ return false;
+ }
+
+ child = array->child;
+ /*
+ * To find the last item in array quickly, we use prev in array
+ */
+ if (child == NULL)
+ {
+ /* list is empty, start new one */
+ array->child = item;
+ item->prev = item;
+ item->next = NULL;
+ }
+ else
+ {
+ /* append to the end */
+ if (child->prev)
+ {
+ suffix_object(child->prev, item);
+ array->child->prev = item;
+ }
+ }
+
+ return true;
+}
+
+/* Add item to array/object. */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item)
+{
+ return add_item_to_array(array, item);
+}
+
+#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
+ #pragma GCC diagnostic push
+#endif
+#ifdef __GNUC__
+#pragma GCC diagnostic ignored "-Wcast-qual"
+#endif
+/* helper function to cast away const */
+static void* cast_away_const(const void* string)
+{
+ return (void*)string;
+}
+#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5))))
+ #pragma GCC diagnostic pop
+#endif
+
+
+static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key)
+{
+ char *new_key = NULL;
+ int new_type = cJSON_Invalid;
+
+ if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item))
+ {
+ return false;
+ }
+
+ if (constant_key)
+ {
+ new_key = (char*)cast_away_const(string);
+ new_type = item->type | cJSON_StringIsConst;
+ }
+ else
+ {
+ new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks);
+ if (new_key == NULL)
+ {
+ return false;
+ }
+
+ new_type = item->type & ~cJSON_StringIsConst;
+ }
+
+ if (!(item->type & cJSON_StringIsConst) && (item->string != NULL))
+ {
+ hooks->deallocate(item->string);
+ }
+
+ item->string = new_key;
+ item->type = new_type;
+
+ return add_item_to_array(object, item);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item)
+{
+ return add_item_to_object(object, string, item, &global_hooks, false);
+}
+
+/* Add an item to an object with constant string as key */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item)
+{
+ return add_item_to_object(object, string, item, &global_hooks, true);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item)
+{
+ if (array == NULL)
+ {
+ return false;
+ }
+
+ return add_item_to_array(array, create_reference(item, &global_hooks));
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item)
+{
+ if ((object == NULL) || (string == NULL))
+ {
+ return false;
+ }
+
+ return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false);
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name)
+{
+ cJSON *null = cJSON_CreateNull();
+ if (add_item_to_object(object, name, null, &global_hooks, false))
+ {
+ return null;
+ }
+
+ cJSON_Delete(null);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name)
+{
+ cJSON *true_item = cJSON_CreateTrue();
+ if (add_item_to_object(object, name, true_item, &global_hooks, false))
+ {
+ return true_item;
+ }
+
+ cJSON_Delete(true_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name)
+{
+ cJSON *false_item = cJSON_CreateFalse();
+ if (add_item_to_object(object, name, false_item, &global_hooks, false))
+ {
+ return false_item;
+ }
+
+ cJSON_Delete(false_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean)
+{
+ cJSON *bool_item = cJSON_CreateBool(boolean);
+ if (add_item_to_object(object, name, bool_item, &global_hooks, false))
+ {
+ return bool_item;
+ }
+
+ cJSON_Delete(bool_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number)
+{
+ cJSON *number_item = cJSON_CreateNumber(number);
+ if (add_item_to_object(object, name, number_item, &global_hooks, false))
+ {
+ return number_item;
+ }
+
+ cJSON_Delete(number_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string)
+{
+ cJSON *string_item = cJSON_CreateString(string);
+ if (add_item_to_object(object, name, string_item, &global_hooks, false))
+ {
+ return string_item;
+ }
+
+ cJSON_Delete(string_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw)
+{
+ cJSON *raw_item = cJSON_CreateRaw(raw);
+ if (add_item_to_object(object, name, raw_item, &global_hooks, false))
+ {
+ return raw_item;
+ }
+
+ cJSON_Delete(raw_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name)
+{
+ cJSON *object_item = cJSON_CreateObject();
+ if (add_item_to_object(object, name, object_item, &global_hooks, false))
+ {
+ return object_item;
+ }
+
+ cJSON_Delete(object_item);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name)
+{
+ cJSON *array = cJSON_CreateArray();
+ if (add_item_to_object(object, name, array, &global_hooks, false))
+ {
+ return array;
+ }
+
+ cJSON_Delete(array);
+ return NULL;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item)
+{
+ if ((parent == NULL) || (item == NULL))
+ {
+ return NULL;
+ }
+
+ if (item != parent->child)
+ {
+ /* not the first element */
+ item->prev->next = item->next;
+ }
+ if (item->next != NULL)
+ {
+ /* not the last element */
+ item->next->prev = item->prev;
+ }
+
+ if (item == parent->child)
+ {
+ /* first element */
+ parent->child = item->next;
+ }
+ else if (item->next == NULL)
+ {
+ /* last element */
+ parent->child->prev = item->prev;
+ }
+
+ /* make sure the detached item doesn't point anywhere anymore */
+ item->prev = NULL;
+ item->next = NULL;
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which)
+{
+ if (which < 0)
+ {
+ return NULL;
+ }
+
+ return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which));
+}
+
+CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which)
+{
+ cJSON_Delete(cJSON_DetachItemFromArray(array, which));
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string)
+{
+ cJSON *to_detach = cJSON_GetObjectItem(object, string);
+
+ return cJSON_DetachItemViaPointer(object, to_detach);
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string)
+{
+ cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string);
+
+ return cJSON_DetachItemViaPointer(object, to_detach);
+}
+
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string)
+{
+ cJSON_Delete(cJSON_DetachItemFromObject(object, string));
+}
+
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string)
+{
+ cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string));
+}
+
+/* Replace array/object items with new ones. */
+CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem)
+{
+ cJSON *after_inserted = NULL;
+
+ if (which < 0 || newitem == NULL)
+ {
+ return false;
+ }
+
+ after_inserted = get_array_item(array, (size_t)which);
+ if (after_inserted == NULL)
+ {
+ return add_item_to_array(array, newitem);
+ }
+
+ if (after_inserted != array->child && after_inserted->prev == NULL) {
+ /* return false if after_inserted is a corrupted array item */
+ return false;
+ }
+
+ newitem->next = after_inserted;
+ newitem->prev = after_inserted->prev;
+ after_inserted->prev = newitem;
+ if (after_inserted == array->child)
+ {
+ array->child = newitem;
+ }
+ else
+ {
+ newitem->prev->next = newitem;
+ }
+ return true;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement)
+{
+ if ((parent == NULL) || (parent->child == NULL) || (replacement == NULL) || (item == NULL))
+ {
+ return false;
+ }
+
+ if (replacement == item)
+ {
+ return true;
+ }
+
+ replacement->next = item->next;
+ replacement->prev = item->prev;
+
+ if (replacement->next != NULL)
+ {
+ replacement->next->prev = replacement;
+ }
+ if (parent->child == item)
+ {
+ if (parent->child->prev == parent->child)
+ {
+ replacement->prev = replacement;
+ }
+ parent->child = replacement;
+ }
+ else
+ { /*
+ * To find the last item in array quickly, we use prev in array.
+ * We can't modify the last item's next pointer where this item was the parent's child
+ */
+ if (replacement->prev != NULL)
+ {
+ replacement->prev->next = replacement;
+ }
+ if (replacement->next == NULL)
+ {
+ parent->child->prev = replacement;
+ }
+ }
+
+ item->next = NULL;
+ item->prev = NULL;
+ cJSON_Delete(item);
+
+ return true;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem)
+{
+ if (which < 0)
+ {
+ return false;
+ }
+
+ return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem);
+}
+
+static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive)
+{
+ if ((replacement == NULL) || (string == NULL))
+ {
+ return false;
+ }
+
+ /* replace the name in the replacement */
+ if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL))
+ {
+ cJSON_free(replacement->string);
+ }
+ replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
+ if (replacement->string == NULL)
+ {
+ return false;
+ }
+
+ replacement->type &= ~cJSON_StringIsConst;
+
+ return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem)
+{
+ return replace_item_in_object(object, string, newitem, false);
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem)
+{
+ return replace_item_in_object(object, string, newitem, true);
+}
+
+/* Create basic types: */
+CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type = cJSON_NULL;
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type = cJSON_True;
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type = cJSON_False;
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type = boolean ? cJSON_True : cJSON_False;
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type = cJSON_Number;
+ item->valuedouble = num;
+
+ /* use saturation in case of overflow */
+ if (num >= INT_MAX)
+ {
+ item->valueint = INT_MAX;
+ }
+ else if (num <= (double)INT_MIN)
+ {
+ item->valueint = INT_MIN;
+ }
+ else
+ {
+ item->valueint = (int)num;
+ }
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type = cJSON_String;
+ item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks);
+ if(!item->valuestring)
+ {
+ cJSON_Delete(item);
+ return NULL;
+ }
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if (item != NULL)
+ {
+ item->type = cJSON_String | cJSON_IsReference;
+ item->valuestring = (char*)cast_away_const(string);
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if (item != NULL) {
+ item->type = cJSON_Object | cJSON_IsReference;
+ item->child = (cJSON*)cast_away_const(child);
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) {
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if (item != NULL) {
+ item->type = cJSON_Array | cJSON_IsReference;
+ item->child = (cJSON*)cast_away_const(child);
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type = cJSON_Raw;
+ item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks);
+ if(!item->valuestring)
+ {
+ cJSON_Delete(item);
+ return NULL;
+ }
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if(item)
+ {
+ item->type=cJSON_Array;
+ }
+
+ return item;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void)
+{
+ cJSON *item = cJSON_New_Item(&global_hooks);
+ if (item)
+ {
+ item->type = cJSON_Object;
+ }
+
+ return item;
+}
+
+/* Create Arrays: */
+CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count)
+{
+ size_t i = 0;
+ cJSON *n = NULL;
+ cJSON *p = NULL;
+ cJSON *a = NULL;
+
+ if ((count < 0) || (numbers == NULL))
+ {
+ return NULL;
+ }
+
+ a = cJSON_CreateArray();
+
+ for(i = 0; a && (i < (size_t)count); i++)
+ {
+ n = cJSON_CreateNumber(numbers[i]);
+ if (!n)
+ {
+ cJSON_Delete(a);
+ return NULL;
+ }
+ if(!i)
+ {
+ a->child = n;
+ }
+ else
+ {
+ suffix_object(p, n);
+ }
+ p = n;
+ }
+
+ if (a && a->child) {
+ a->child->prev = n;
+ }
+
+ return a;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count)
+{
+ size_t i = 0;
+ cJSON *n = NULL;
+ cJSON *p = NULL;
+ cJSON *a = NULL;
+
+ if ((count < 0) || (numbers == NULL))
+ {
+ return NULL;
+ }
+
+ a = cJSON_CreateArray();
+
+ for(i = 0; a && (i < (size_t)count); i++)
+ {
+ n = cJSON_CreateNumber((double)numbers[i]);
+ if(!n)
+ {
+ cJSON_Delete(a);
+ return NULL;
+ }
+ if(!i)
+ {
+ a->child = n;
+ }
+ else
+ {
+ suffix_object(p, n);
+ }
+ p = n;
+ }
+
+ if (a && a->child) {
+ a->child->prev = n;
+ }
+
+ return a;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count)
+{
+ size_t i = 0;
+ cJSON *n = NULL;
+ cJSON *p = NULL;
+ cJSON *a = NULL;
+
+ if ((count < 0) || (numbers == NULL))
+ {
+ return NULL;
+ }
+
+ a = cJSON_CreateArray();
+
+ for(i = 0; a && (i < (size_t)count); i++)
+ {
+ n = cJSON_CreateNumber(numbers[i]);
+ if(!n)
+ {
+ cJSON_Delete(a);
+ return NULL;
+ }
+ if(!i)
+ {
+ a->child = n;
+ }
+ else
+ {
+ suffix_object(p, n);
+ }
+ p = n;
+ }
+
+ if (a && a->child) {
+ a->child->prev = n;
+ }
+
+ return a;
+}
+
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count)
+{
+ size_t i = 0;
+ cJSON *n = NULL;
+ cJSON *p = NULL;
+ cJSON *a = NULL;
+
+ if ((count < 0) || (strings == NULL))
+ {
+ return NULL;
+ }
+
+ a = cJSON_CreateArray();
+
+ for (i = 0; a && (i < (size_t)count); i++)
+ {
+ n = cJSON_CreateString(strings[i]);
+ if(!n)
+ {
+ cJSON_Delete(a);
+ return NULL;
+ }
+ if(!i)
+ {
+ a->child = n;
+ }
+ else
+ {
+ suffix_object(p,n);
+ }
+ p = n;
+ }
+
+ if (a && a->child) {
+ a->child->prev = n;
+ }
+
+ return a;
+}
+
+/* Duplication */
+CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse)
+{
+ cJSON *newitem = NULL;
+ cJSON *child = NULL;
+ cJSON *next = NULL;
+ cJSON *newchild = NULL;
+
+ /* Bail on bad ptr */
+ if (!item)
+ {
+ goto fail;
+ }
+ /* Create new item */
+ newitem = cJSON_New_Item(&global_hooks);
+ if (!newitem)
+ {
+ goto fail;
+ }
+ /* Copy over all vars */
+ newitem->type = item->type & (~cJSON_IsReference);
+ newitem->valueint = item->valueint;
+ newitem->valuedouble = item->valuedouble;
+ if (item->valuestring)
+ {
+ newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks);
+ if (!newitem->valuestring)
+ {
+ goto fail;
+ }
+ }
+ if (item->string)
+ {
+ newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks);
+ if (!newitem->string)
+ {
+ goto fail;
+ }
+ }
+ /* If non-recursive, then we're done! */
+ if (!recurse)
+ {
+ return newitem;
+ }
+ /* Walk the ->next chain for the child. */
+ child = item->child;
+ while (child != NULL)
+ {
+ newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */
+ if (!newchild)
+ {
+ goto fail;
+ }
+ if (next != NULL)
+ {
+ /* If newitem->child already set, then crosswire ->prev and ->next and move on */
+ next->next = newchild;
+ newchild->prev = next;
+ next = newchild;
+ }
+ else
+ {
+ /* Set newitem->child and move to it */
+ newitem->child = newchild;
+ next = newchild;
+ }
+ child = child->next;
+ }
+ if (newitem && newitem->child)
+ {
+ newitem->child->prev = newchild;
+ }
+
+ return newitem;
+
+fail:
+ if (newitem != NULL)
+ {
+ cJSON_Delete(newitem);
+ }
+
+ return NULL;
+}
+
+static void skip_oneline_comment(char **input)
+{
+ *input += static_strlen("//");
+
+ for (; (*input)[0] != '\0'; ++(*input))
+ {
+ if ((*input)[0] == '\n') {
+ *input += static_strlen("\n");
+ return;
+ }
+ }
+}
+
+static void skip_multiline_comment(char **input)
+{
+ *input += static_strlen("/*");
+
+ for (; (*input)[0] != '\0'; ++(*input))
+ {
+ if (((*input)[0] == '*') && ((*input)[1] == '/'))
+ {
+ *input += static_strlen("*/");
+ return;
+ }
+ }
+}
+
+static void minify_string(char **input, char **output) {
+ (*output)[0] = (*input)[0];
+ *input += static_strlen("\"");
+ *output += static_strlen("\"");
+
+
+ for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) {
+ (*output)[0] = (*input)[0];
+
+ if ((*input)[0] == '\"') {
+ (*output)[0] = '\"';
+ *input += static_strlen("\"");
+ *output += static_strlen("\"");
+ return;
+ } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) {
+ (*output)[1] = (*input)[1];
+ *input += static_strlen("\"");
+ *output += static_strlen("\"");
+ }
+ }
+}
+
+CJSON_PUBLIC(void) cJSON_Minify(char *json)
+{
+ char *into = json;
+
+ if (json == NULL)
+ {
+ return;
+ }
+
+ while (json[0] != '\0')
+ {
+ switch (json[0])
+ {
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ json++;
+ break;
+
+ case '/':
+ if (json[1] == '/')
+ {
+ skip_oneline_comment(&json);
+ }
+ else if (json[1] == '*')
+ {
+ skip_multiline_comment(&json);
+ } else {
+ json++;
+ }
+ break;
+
+ case '\"':
+ minify_string(&json, (char**)&into);
+ break;
+
+ default:
+ into[0] = json[0];
+ json++;
+ into++;
+ }
+ }
+
+ /* and null-terminate. */
+ *into = '\0';
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_Invalid;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_False;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xff) == cJSON_True;
+}
+
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & (cJSON_True | cJSON_False)) != 0;
+}
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_NULL;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_Number;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_String;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_Array;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_Object;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item)
+{
+ if (item == NULL)
+ {
+ return false;
+ }
+
+ return (item->type & 0xFF) == cJSON_Raw;
+}
+
+CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive)
+{
+ if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)))
+ {
+ return false;
+ }
+
+ /* check if type is valid */
+ switch (a->type & 0xFF)
+ {
+ case cJSON_False:
+ case cJSON_True:
+ case cJSON_NULL:
+ case cJSON_Number:
+ case cJSON_String:
+ case cJSON_Raw:
+ case cJSON_Array:
+ case cJSON_Object:
+ break;
+
+ default:
+ return false;
+ }
+
+ /* identical objects are equal */
+ if (a == b)
+ {
+ return true;
+ }
+
+ switch (a->type & 0xFF)
+ {
+ /* in these cases and equal type is enough */
+ case cJSON_False:
+ case cJSON_True:
+ case cJSON_NULL:
+ return true;
+
+ case cJSON_Number:
+ if (compare_double(a->valuedouble, b->valuedouble))
+ {
+ return true;
+ }
+ return false;
+
+ case cJSON_String:
+ case cJSON_Raw:
+ if ((a->valuestring == NULL) || (b->valuestring == NULL))
+ {
+ return false;
+ }
+ if (strcmp(a->valuestring, b->valuestring) == 0)
+ {
+ return true;
+ }
+
+ return false;
+
+ case cJSON_Array:
+ {
+ cJSON *a_element = a->child;
+ cJSON *b_element = b->child;
+
+ for (; (a_element != NULL) && (b_element != NULL);)
+ {
+ if (!cJSON_Compare(a_element, b_element, case_sensitive))
+ {
+ return false;
+ }
+
+ a_element = a_element->next;
+ b_element = b_element->next;
+ }
+
+ /* one of the arrays is longer than the other */
+ if (a_element != b_element) {
+ return false;
+ }
+
+ return true;
+ }
+
+ case cJSON_Object:
+ {
+ cJSON *a_element = NULL;
+ cJSON *b_element = NULL;
+ cJSON_ArrayForEach(a_element, a)
+ {
+ /* TODO This has O(n^2) runtime, which is horrible! */
+ b_element = get_object_item(b, a_element->string, case_sensitive);
+ if (b_element == NULL)
+ {
+ return false;
+ }
+
+ if (!cJSON_Compare(a_element, b_element, case_sensitive))
+ {
+ return false;
+ }
+ }
+
+ /* doing this twice, once on a and b to prevent true comparison if a subset of b
+ * TODO: Do this the proper way, this is just a fix for now */
+ cJSON_ArrayForEach(b_element, b)
+ {
+ a_element = get_object_item(a, b_element->string, case_sensitive);
+ if (a_element == NULL)
+ {
+ return false;
+ }
+
+ if (!cJSON_Compare(b_element, a_element, case_sensitive))
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ default:
+ return false;
+ }
+}
+
+CJSON_PUBLIC(void *) cJSON_malloc(size_t size)
+{
+ return global_hooks.allocate(size);
+}
+
+CJSON_PUBLIC(void) cJSON_free(void *object)
+{
+ global_hooks.deallocate(object);
+}
diff --git a/packages/cJSON-v1.7.17/cJSON.h b/packages/cJSON-v1.7.17/cJSON.h
new file mode 100644
index 0000000..4d748c6
--- /dev/null
+++ b/packages/cJSON-v1.7.17/cJSON.h
@@ -0,0 +1,300 @@
+/*
+ Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+#ifndef cJSON__h
+#define cJSON__h
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32))
+#define __WINDOWS__
+#endif
+
+#ifdef __WINDOWS__
+
+/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options:
+
+CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols
+CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default)
+CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol
+
+For *nix builds that support visibility attribute, you can define similar behavior by
+
+setting default visibility to hidden by adding
+-fvisibility=hidden (for gcc)
+or
+-xldscope=hidden (for sun cc)
+to CFLAGS
+
+then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does
+
+*/
+
+#define CJSON_CDECL __cdecl
+#define CJSON_STDCALL __stdcall
+
+/* export symbols by default, this is necessary for copy pasting the C and header file */
+#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS)
+#define CJSON_EXPORT_SYMBOLS
+#endif
+
+#if defined(CJSON_HIDE_SYMBOLS)
+#define CJSON_PUBLIC(type) type CJSON_STDCALL
+#elif defined(CJSON_EXPORT_SYMBOLS)
+#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL
+#elif defined(CJSON_IMPORT_SYMBOLS)
+#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL
+#endif
+#else /* !__WINDOWS__ */
+#define CJSON_CDECL
+#define CJSON_STDCALL
+
+#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY)
+#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type
+#else
+#define CJSON_PUBLIC(type) type
+#endif
+#endif
+
+/* project version */
+#define CJSON_VERSION_MAJOR 1
+#define CJSON_VERSION_MINOR 7
+#define CJSON_VERSION_PATCH 17
+
+#include
+
+/* cJSON Types: */
+#define cJSON_Invalid (0)
+#define cJSON_False (1 << 0)
+#define cJSON_True (1 << 1)
+#define cJSON_NULL (1 << 2)
+#define cJSON_Number (1 << 3)
+#define cJSON_String (1 << 4)
+#define cJSON_Array (1 << 5)
+#define cJSON_Object (1 << 6)
+#define cJSON_Raw (1 << 7) /* raw json */
+
+#define cJSON_IsReference 256
+#define cJSON_StringIsConst 512
+
+/* The cJSON structure: */
+typedef struct cJSON
+{
+ /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
+ struct cJSON *next;
+ struct cJSON *prev;
+ /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
+ struct cJSON *child;
+
+ /* The type of the item, as above. */
+ int type;
+
+ /* The item's string, if type==cJSON_String and type == cJSON_Raw */
+ char *valuestring;
+ /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
+ int valueint;
+ /* The item's number, if type==cJSON_Number */
+ double valuedouble;
+
+ /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
+ char *string;
+} cJSON;
+
+typedef struct cJSON_Hooks
+{
+ /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */
+ void *(CJSON_CDECL *malloc_fn)(size_t sz);
+ void (CJSON_CDECL *free_fn)(void *ptr);
+} cJSON_Hooks;
+
+typedef int cJSON_bool;
+
+/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them.
+ * This is to prevent stack overflows. */
+#ifndef CJSON_NESTING_LIMIT
+#define CJSON_NESTING_LIMIT 1000
+#endif
+
+/* returns the version of cJSON as a string */
+CJSON_PUBLIC(const char*) cJSON_Version(void);
+
+/* Supply malloc, realloc and free functions to cJSON */
+CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks);
+
+/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */
+/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */
+CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
+/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */
+/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated);
+CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated);
+
+/* Render a cJSON entity to text for transfer/storage. */
+CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
+/* Render a cJSON entity to text for transfer/storage without any formatting. */
+CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item);
+/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */
+CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt);
+/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */
+/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */
+CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format);
+/* Delete a cJSON entity and all subentities. */
+CJSON_PUBLIC(void) cJSON_Delete(cJSON *item);
+
+/* Returns the number of items in an array (or object). */
+CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
+/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */
+CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
+/* Get item "string" from object. Case insensitive. */
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
+CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string);
+CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
+/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */
+CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void);
+
+/* Check item type and return its value */
+CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item);
+CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item);
+
+/* These functions check the type of an item */
+CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item);
+CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item);
+
+/* These calls create a cJSON item of the appropriate type. */
+CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
+CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
+CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
+/* raw json */
+CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
+CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
+CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void);
+
+/* Create a string where valuestring references a string so
+ * it will not be freed by cJSON_Delete */
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string);
+/* Create an object/array that only references it's elements so
+ * they will not be freed by cJSON_Delete */
+CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child);
+CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child);
+
+/* These utilities create an Array of count items.
+ * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/
+CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count);
+CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count);
+CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count);
+CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count);
+
+/* Append item to the specified array/object. */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item);
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
+/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object.
+ * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before
+ * writing to `item->string` */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item);
+/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
+CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item);
+
+/* Remove/Detach items from Arrays/Objects. */
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
+CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
+CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
+CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
+
+/* Update array items. */
+CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement);
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem);
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
+CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem);
+
+/* Duplicate a cJSON item */
+CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse);
+/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will
+ * need to be released. With recurse!=0, it will duplicate any children connected to the item.
+ * The item->next and ->prev pointers are always zero on return from Duplicate. */
+/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal.
+ * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */
+CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive);
+
+/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings.
+ * The input pointer json cannot point to a read-only address area, such as a string constant,
+ * but should point to a readable and writable address area. */
+CJSON_PUBLIC(void) cJSON_Minify(char *json);
+
+/* Helper functions for creating and adding items to an object at the same time.
+ * They return the added item or NULL on failure. */
+CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
+CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
+CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
+CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
+CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
+CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);
+
+/* When assigning an integer value, it needs to be propagated to valuedouble too. */
+#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number))
+/* helper for the cJSON_SetNumberValue macro */
+CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number);
+#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number))
+/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */
+CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring);
+
+/* If the object is not a boolean type this does nothing and returns cJSON_Invalid else it returns the new type*/
+#define cJSON_SetBoolValue(object, boolValue) ( \
+ (object != NULL && ((object)->type & (cJSON_False|cJSON_True))) ? \
+ (object)->type=((object)->type &(~(cJSON_False|cJSON_True)))|((boolValue)?cJSON_True:cJSON_False) : \
+ cJSON_Invalid\
+)
+
+/* Macro for iterating over an array or object */
+#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
+
+/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */
+CJSON_PUBLIC(void *) cJSON_malloc(size_t size);
+CJSON_PUBLIC(void) cJSON_free(void *object);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/packages/cJSON-v1.7.17/cJSON_Utils.c b/packages/cJSON-v1.7.17/cJSON_Utils.c
new file mode 100644
index 0000000..53ca9ee
--- /dev/null
+++ b/packages/cJSON-v1.7.17/cJSON_Utils.c
@@ -0,0 +1,1482 @@
+/*
+ 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.
+*/
+
+/* disable warnings about old C89 functions in MSVC */
+#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER)
+#define _CRT_SECURE_NO_DEPRECATE
+#endif
+
+#ifdef __GNUCC__
+#pragma GCC visibility push(default)
+#endif
+#if defined(_MSC_VER)
+#pragma warning (push)
+/* disable warning about single line comments in system headers */
+#pragma warning (disable : 4001)
+#endif
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#if defined(_MSC_VER)
+#pragma warning (pop)
+#endif
+#ifdef __GNUCC__
+#pragma GCC visibility pop
+#endif
+
+#include
+#include "cJSON_Utils.h"
+
+/* define our own boolean type */
+#ifdef true
+#undef true
+#endif
+#define true ((cJSON_bool)1)
+
+#ifdef false
+#undef false
+#endif
+#define false ((cJSON_bool)0)
+
+static unsigned char* cJSONUtils_strdup(const unsigned char* const string)
+{
+ size_t length = 0;
+ unsigned char *copy = NULL;
+
+ length = strlen((const char*)string) + sizeof("");
+ copy = (unsigned char*) cJSON_malloc(length);
+ if (copy == NULL)
+ {
+ return NULL;
+ }
+ rt_memcpy(copy, string, length);
+
+ return copy;
+}
+
+/* string comparison which doesn't consider NULL pointers equal */
+static int compare_strings(const unsigned char *string1, const unsigned char *string2, const cJSON_bool case_sensitive)
+{
+ if ((string1 == NULL) || (string2 == NULL))
+ {
+ return 1;
+ }
+
+ if (string1 == string2)
+ {
+ return 0;
+ }
+
+ if (case_sensitive)
+ {
+ return strcmp((const char*)string1, (const char*)string2);
+ }
+
+ for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++)
+ {
+ if (*string1 == '\0')
+ {
+ return 0;
+ }
+ }
+
+ return tolower(*string1) - tolower(*string2);
+}
+
+/* securely comparison of floating-point variables */
+static cJSON_bool compare_double(double a, double b)
+{
+ double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b);
+ return (fabs(a - b) <= maxVal * DBL_EPSILON);
+}
+
+
+/* Compare the next path element of two JSON pointers, two NULL pointers are considered unequal: */
+static cJSON_bool compare_pointers(const unsigned char *name, const unsigned char *pointer, const cJSON_bool case_sensitive)
+{
+ if ((name == NULL) || (pointer == NULL))
+ {
+ return false;
+ }
+
+ for (; (*name != '\0') && (*pointer != '\0') && (*pointer != '/'); (void)name++, pointer++) /* compare until next '/' */
+ {
+ if (*pointer == '~')
+ {
+ /* check for escaped '~' (~0) and '/' (~1) */
+ if (((pointer[1] != '0') || (*name != '~')) && ((pointer[1] != '1') || (*name != '/')))
+ {
+ /* invalid escape sequence or wrong character in *name */
+ return false;
+ }
+ else
+ {
+ pointer++;
+ }
+ }
+ else if ((!case_sensitive && (tolower(*name) != tolower(*pointer))) || (case_sensitive && (*name != *pointer)))
+ {
+ return false;
+ }
+ }
+ if (((*pointer != 0) && (*pointer != '/')) != (*name != 0))
+ {
+ /* one string has ended, the other not */
+ return false;;
+ }
+
+ return true;
+}
+
+/* calculate the length of a string if encoded as JSON pointer with ~0 and ~1 escape sequences */
+static size_t pointer_encoded_length(const unsigned char *string)
+{
+ size_t length;
+ for (length = 0; *string != '\0'; (void)string++, length++)
+ {
+ /* character needs to be escaped? */
+ if ((*string == '~') || (*string == '/'))
+ {
+ length++;
+ }
+ }
+
+ return length;
+}
+
+/* copy a string while escaping '~' and '/' with ~0 and ~1 JSON pointer escape codes */
+static void encode_string_as_pointer(unsigned char *destination, const unsigned char *source)
+{
+ for (; source[0] != '\0'; (void)source++, destination++)
+ {
+ if (source[0] == '/')
+ {
+ destination[0] = '~';
+ destination[1] = '1';
+ destination++;
+ }
+ else if (source[0] == '~')
+ {
+ destination[0] = '~';
+ destination[1] = '0';
+ destination++;
+ }
+ else
+ {
+ destination[0] = source[0];
+ }
+ }
+
+ destination[0] = '\0';
+}
+
+CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(const cJSON * const object, const cJSON * const target)
+{
+ size_t child_index = 0;
+ cJSON *current_child = 0;
+
+ if ((object == NULL) || (target == NULL))
+ {
+ return NULL;
+ }
+
+ if (object == target)
+ {
+ /* found */
+ return (char*)cJSONUtils_strdup((const unsigned char*)"");
+ }
+
+ /* recursively search all children of the object or array */
+ for (current_child = object->child; current_child != NULL; (void)(current_child = current_child->next), child_index++)
+ {
+ unsigned char *target_pointer = (unsigned char*)cJSONUtils_FindPointerFromObjectTo(current_child, target);
+ /* found the target? */
+ if (target_pointer != NULL)
+ {
+ if (cJSON_IsArray(object))
+ {
+ /* reserve enough memory for a 64 bit integer + '/' and '\0' */
+ unsigned char *full_pointer = (unsigned char*)cJSON_malloc(strlen((char*)target_pointer) + 20 + sizeof("/"));
+ /* check if conversion to unsigned long is valid
+ * This should be eliminated at compile time by dead code elimination
+ * if size_t is an alias of unsigned long, or if it is bigger */
+ if (child_index > ULONG_MAX)
+ {
+ cJSON_free(target_pointer);
+ cJSON_free(full_pointer);
+ return NULL;
+ }
+ sprintf((char*)full_pointer, "/%lu%s", (unsigned long)child_index, target_pointer); /* / */
+ cJSON_free(target_pointer);
+
+ return (char*)full_pointer;
+ }
+
+ if (cJSON_IsObject(object))
+ {
+ unsigned char *full_pointer = (unsigned char*)cJSON_malloc(strlen((char*)target_pointer) + pointer_encoded_length((unsigned char*)current_child->string) + 2);
+ full_pointer[0] = '/';
+ encode_string_as_pointer(full_pointer + 1, (unsigned char*)current_child->string);
+ strcat((char*)full_pointer, (char*)target_pointer);
+ cJSON_free(target_pointer);
+
+ return (char*)full_pointer;
+ }
+
+ /* reached leaf of the tree, found nothing */
+ cJSON_free(target_pointer);
+ return NULL;
+ }
+ }
+
+ /* not found */
+ return NULL;
+}
+
+/* non broken version of cJSON_GetArrayItem */
+static cJSON *get_array_item(const cJSON *array, size_t item)
+{
+ cJSON *child = array ? array->child : NULL;
+ while ((child != NULL) && (item > 0))
+ {
+ item--;
+ child = child->next;
+ }
+
+ return child;
+}
+
+static cJSON_bool decode_array_index_from_pointer(const unsigned char * const pointer, size_t * const index)
+{
+ size_t parsed_index = 0;
+ size_t position = 0;
+
+ if ((pointer[0] == '0') && ((pointer[1] != '\0') && (pointer[1] != '/')))
+ {
+ /* leading zeroes are not permitted */
+ return 0;
+ }
+
+ for (position = 0; (pointer[position] >= '0') && (pointer[0] <= '9'); position++)
+ {
+ parsed_index = (10 * parsed_index) + (size_t)(pointer[position] - '0');
+
+ }
+
+ if ((pointer[position] != '\0') && (pointer[position] != '/'))
+ {
+ return 0;
+ }
+
+ *index = parsed_index;
+
+ return 1;
+}
+
+static cJSON *get_item_from_pointer(cJSON * const object, const char * pointer, const cJSON_bool case_sensitive)
+{
+ cJSON *current_element = object;
+
+ if (pointer == NULL)
+ {
+ return NULL;
+ }
+
+ /* follow path of the pointer */
+ while ((pointer[0] == '/') && (current_element != NULL))
+ {
+ pointer++;
+ if (cJSON_IsArray(current_element))
+ {
+ size_t index = 0;
+ if (!decode_array_index_from_pointer((const unsigned char*)pointer, &index))
+ {
+ return NULL;
+ }
+
+ current_element = get_array_item(current_element, index);
+ }
+ else if (cJSON_IsObject(current_element))
+ {
+ current_element = current_element->child;
+ /* GetObjectItem. */
+ while ((current_element != NULL) && !compare_pointers((unsigned char*)current_element->string, (const unsigned char*)pointer, case_sensitive))
+ {
+ current_element = current_element->next;
+ }
+ }
+ else
+ {
+ return NULL;
+ }
+
+ /* skip to the next path token or end of string */
+ while ((pointer[0] != '\0') && (pointer[0] != '/'))
+ {
+ pointer++;
+ }
+ }
+
+ return current_element;
+}
+
+CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointer(cJSON * const object, const char *pointer)
+{
+ return get_item_from_pointer(object, pointer, false);
+}
+
+CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointerCaseSensitive(cJSON * const object, const char *pointer)
+{
+ return get_item_from_pointer(object, pointer, true);
+}
+
+/* JSON Patch implementation. */
+static void decode_pointer_inplace(unsigned char *string)
+{
+ unsigned char *decoded_string = string;
+
+ if (string == NULL) {
+ return;
+ }
+
+ for (; *string; (void)decoded_string++, string++)
+ {
+ if (string[0] == '~')
+ {
+ if (string[1] == '0')
+ {
+ decoded_string[0] = '~';
+ }
+ else if (string[1] == '1')
+ {
+ decoded_string[1] = '/';
+ }
+ else
+ {
+ /* invalid escape sequence */
+ return;
+ }
+
+ string++;
+ }
+ }
+
+ decoded_string[0] = '\0';
+}
+
+/* non-broken cJSON_DetachItemFromArray */
+static cJSON *detach_item_from_array(cJSON *array, size_t which)
+{
+ cJSON *c = array->child;
+ while (c && (which > 0))
+ {
+ c = c->next;
+ which--;
+ }
+ if (!c)
+ {
+ /* item doesn't exist */
+ return NULL;
+ }
+ if (c != array->child)
+ {
+ /* not the first element */
+ c->prev->next = c->next;
+ }
+ if (c->next)
+ {
+ c->next->prev = c->prev;
+ }
+ if (c == array->child)
+ {
+ array->child = c->next;
+ }
+ else if (c->next == NULL)
+ {
+ array->child->prev = c->prev;
+ }
+ /* make sure the detached item doesn't point anywhere anymore */
+ c->prev = c->next = NULL;
+
+ return c;
+}
+
+/* detach an item at the given path */
+static cJSON *detach_path(cJSON *object, const unsigned char *path, const cJSON_bool case_sensitive)
+{
+ unsigned char *parent_pointer = NULL;
+ unsigned char *child_pointer = NULL;
+ cJSON *parent = NULL;
+ cJSON *detached_item = NULL;
+
+ /* copy path and split it in parent and child */
+ parent_pointer = cJSONUtils_strdup(path);
+ if (parent_pointer == NULL) {
+ goto cleanup;
+ }
+
+ child_pointer = (unsigned char*)strrchr((char*)parent_pointer, '/'); /* last '/' */
+ if (child_pointer == NULL)
+ {
+ goto cleanup;
+ }
+ /* split strings */
+ child_pointer[0] = '\0';
+ child_pointer++;
+
+ parent = get_item_from_pointer(object, (char*)parent_pointer, case_sensitive);
+ decode_pointer_inplace(child_pointer);
+
+ if (cJSON_IsArray(parent))
+ {
+ size_t index = 0;
+ if (!decode_array_index_from_pointer(child_pointer, &index))
+ {
+ goto cleanup;
+ }
+ detached_item = detach_item_from_array(parent, index);
+ }
+ else if (cJSON_IsObject(parent))
+ {
+ detached_item = cJSON_DetachItemFromObject(parent, (char*)child_pointer);
+ }
+ else
+ {
+ /* Couldn't find object to remove child from. */
+ goto cleanup;
+ }
+
+cleanup:
+ if (parent_pointer != NULL)
+ {
+ cJSON_free(parent_pointer);
+ }
+
+ return detached_item;
+}
+
+/* sort lists using mergesort */
+static cJSON *sort_list(cJSON *list, const cJSON_bool case_sensitive)
+{
+ cJSON *first = list;
+ cJSON *second = list;
+ cJSON *current_item = list;
+ cJSON *result = list;
+ cJSON *result_tail = NULL;
+
+ if ((list == NULL) || (list->next == NULL))
+ {
+ /* One entry is sorted already. */
+ return result;
+ }
+
+ while ((current_item != NULL) && (current_item->next != NULL) && (compare_strings((unsigned char*)current_item->string, (unsigned char*)current_item->next->string, case_sensitive) < 0))
+ {
+ /* Test for list sorted. */
+ current_item = current_item->next;
+ }
+ if ((current_item == NULL) || (current_item->next == NULL))
+ {
+ /* Leave sorted lists unmodified. */
+ return result;
+ }
+
+ /* reset pointer to the beginning */
+ current_item = list;
+ while (current_item != NULL)
+ {
+ /* Walk two pointers to find the middle. */
+ second = second->next;
+ current_item = current_item->next;
+ /* advances current_item two steps at a time */
+ if (current_item != NULL)
+ {
+ current_item = current_item->next;
+ }
+ }
+ if ((second != NULL) && (second->prev != NULL))
+ {
+ /* Split the lists */
+ second->prev->next = NULL;
+ second->prev = NULL;
+ }
+
+ /* Recursively sort the sub-lists. */
+ first = sort_list(first, case_sensitive);
+ second = sort_list(second, case_sensitive);
+ result = NULL;
+
+ /* Merge the sub-lists */
+ while ((first != NULL) && (second != NULL))
+ {
+ cJSON *smaller = NULL;
+ if (compare_strings((unsigned char*)first->string, (unsigned char*)second->string, case_sensitive) < 0)
+ {
+ smaller = first;
+ }
+ else
+ {
+ smaller = second;
+ }
+
+ if (result == NULL)
+ {
+ /* start merged list with the smaller element */
+ result_tail = smaller;
+ result = smaller;
+ }
+ else
+ {
+ /* add smaller element to the list */
+ result_tail->next = smaller;
+ smaller->prev = result_tail;
+ result_tail = smaller;
+ }
+
+ if (first == smaller)
+ {
+ first = first->next;
+ }
+ else
+ {
+ second = second->next;
+ }
+ }
+
+ if (first != NULL)
+ {
+ /* Append rest of first list. */
+ if (result == NULL)
+ {
+ return first;
+ }
+ result_tail->next = first;
+ first->prev = result_tail;
+ }
+ if (second != NULL)
+ {
+ /* Append rest of second list */
+ if (result == NULL)
+ {
+ return second;
+ }
+ result_tail->next = second;
+ second->prev = result_tail;
+ }
+
+ return result;
+}
+
+static void sort_object(cJSON * const object, const cJSON_bool case_sensitive)
+{
+ if (object == NULL)
+ {
+ return;
+ }
+ object->child = sort_list(object->child, case_sensitive);
+}
+
+static cJSON_bool compare_json(cJSON *a, cJSON *b, const cJSON_bool case_sensitive)
+{
+ if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)))
+ {
+ /* mismatched type. */
+ return false;
+ }
+ switch (a->type & 0xFF)
+ {
+ case cJSON_Number:
+ /* numeric mismatch. */
+ if ((a->valueint != b->valueint) || (!compare_double(a->valuedouble, b->valuedouble)))
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+
+ case cJSON_String:
+ /* string mismatch. */
+ if (strcmp(a->valuestring, b->valuestring) != 0)
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+
+ case cJSON_Array:
+ for ((void)(a = a->child), b = b->child; (a != NULL) && (b != NULL); (void)(a = a->next), b = b->next)
+ {
+ cJSON_bool identical = compare_json(a, b, case_sensitive);
+ if (!identical)
+ {
+ return false;
+ }
+ }
+
+ /* array size mismatch? (one of both children is not NULL) */
+ if ((a != NULL) || (b != NULL))
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+
+ case cJSON_Object:
+ sort_object(a, case_sensitive);
+ sort_object(b, case_sensitive);
+ for ((void)(a = a->child), b = b->child; (a != NULL) && (b != NULL); (void)(a = a->next), b = b->next)
+ {
+ cJSON_bool identical = false;
+ /* compare object keys */
+ if (compare_strings((unsigned char*)a->string, (unsigned char*)b->string, case_sensitive))
+ {
+ /* missing member */
+ return false;
+ }
+ identical = compare_json(a, b, case_sensitive);
+ if (!identical)
+ {
+ return false;
+ }
+ }
+
+ /* object length mismatch (one of both children is not null) */
+ if ((a != NULL) || (b != NULL))
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+
+ default:
+ break;
+ }
+
+ /* null, true or false */
+ return true;
+}
+
+/* non broken version of cJSON_InsertItemInArray */
+static cJSON_bool insert_item_in_array(cJSON *array, size_t which, cJSON *newitem)
+{
+ cJSON *child = array->child;
+ while (child && (which > 0))
+ {
+ child = child->next;
+ which--;
+ }
+ if (which > 0)
+ {
+ /* item is after the end of the array */
+ return 0;
+ }
+ if (child == NULL)
+ {
+ cJSON_AddItemToArray(array, newitem);
+ return 1;
+ }
+
+ /* insert into the linked list */
+ newitem->next = child;
+ newitem->prev = child->prev;
+ child->prev = newitem;
+
+ /* was it at the beginning */
+ if (child == array->child)
+ {
+ array->child = newitem;
+ }
+ else
+ {
+ newitem->prev->next = newitem;
+ }
+
+ return 1;
+}
+
+static cJSON *get_object_item(const cJSON * const object, const char* name, const cJSON_bool case_sensitive)
+{
+ if (case_sensitive)
+ {
+ return cJSON_GetObjectItemCaseSensitive(object, name);
+ }
+
+ return cJSON_GetObjectItem(object, name);
+}
+
+enum patch_operation { INVALID, ADD, REMOVE, REPLACE, MOVE, COPY, TEST };
+
+static enum patch_operation decode_patch_operation(const cJSON * const patch, const cJSON_bool case_sensitive)
+{
+ cJSON *operation = get_object_item(patch, "op", case_sensitive);
+ if (!cJSON_IsString(operation))
+ {
+ return INVALID;
+ }
+
+ if (strcmp(operation->valuestring, "add") == 0)
+ {
+ return ADD;
+ }
+
+ if (strcmp(operation->valuestring, "remove") == 0)
+ {
+ return REMOVE;
+ }
+
+ if (strcmp(operation->valuestring, "replace") == 0)
+ {
+ return REPLACE;
+ }
+
+ if (strcmp(operation->valuestring, "move") == 0)
+ {
+ return MOVE;
+ }
+
+ if (strcmp(operation->valuestring, "copy") == 0)
+ {
+ return COPY;
+ }
+
+ if (strcmp(operation->valuestring, "test") == 0)
+ {
+ return TEST;
+ }
+
+ return INVALID;
+}
+
+/* overwrite and existing item with another one and free resources on the way */
+static void overwrite_item(cJSON * const root, const cJSON replacement)
+{
+ if (root == NULL)
+ {
+ return;
+ }
+
+ if (root->string != NULL)
+ {
+ cJSON_free(root->string);
+ }
+ if (root->valuestring != NULL)
+ {
+ cJSON_free(root->valuestring);
+ }
+ if (root->child != NULL)
+ {
+ cJSON_Delete(root->child);
+ }
+
+ rt_memcpy(root, &replacement, sizeof(cJSON));
+}
+
+static int apply_patch(cJSON *object, const cJSON *patch, const cJSON_bool case_sensitive)
+{
+ cJSON *path = NULL;
+ cJSON *value = NULL;
+ cJSON *parent = NULL;
+ enum patch_operation opcode = INVALID;
+ unsigned char *parent_pointer = NULL;
+ unsigned char *child_pointer = NULL;
+ int status = 0;
+
+ path = get_object_item(patch, "path", case_sensitive);
+ if (!cJSON_IsString(path))
+ {
+ /* malformed patch. */
+ status = 2;
+ goto cleanup;
+ }
+
+ opcode = decode_patch_operation(patch, case_sensitive);
+ if (opcode == INVALID)
+ {
+ status = 3;
+ goto cleanup;
+ }
+ else if (opcode == TEST)
+ {
+ /* compare value: {...} with the given path */
+ status = !compare_json(get_item_from_pointer(object, path->valuestring, case_sensitive), get_object_item(patch, "value", case_sensitive), case_sensitive);
+ goto cleanup;
+ }
+
+ /* special case for replacing the root */
+ if (path->valuestring[0] == '\0')
+ {
+ if (opcode == REMOVE)
+ {
+ static const cJSON invalid = { NULL, NULL, NULL, cJSON_Invalid, NULL, 0, 0, NULL};
+
+ overwrite_item(object, invalid);
+
+ status = 0;
+ goto cleanup;
+ }
+
+ if ((opcode == REPLACE) || (opcode == ADD))
+ {
+ value = get_object_item(patch, "value", case_sensitive);
+ if (value == NULL)
+ {
+ /* missing "value" for add/replace. */
+ status = 7;
+ goto cleanup;
+ }
+
+ value = cJSON_Duplicate(value, 1);
+ if (value == NULL)
+ {
+ /* out of memory for add/replace. */
+ status = 8;
+ goto cleanup;
+ }
+
+ overwrite_item(object, *value);
+
+ /* delete the duplicated value */
+ cJSON_free(value);
+ value = NULL;
+
+ /* the string "value" isn't needed */
+ if (object->string != NULL)
+ {
+ cJSON_free(object->string);
+ object->string = NULL;
+ }
+
+ status = 0;
+ goto cleanup;
+ }
+ }
+
+ if ((opcode == REMOVE) || (opcode == REPLACE))
+ {
+ /* Get rid of old. */
+ cJSON *old_item = detach_path(object, (unsigned char*)path->valuestring, case_sensitive);
+ if (old_item == NULL)
+ {
+ status = 13;
+ goto cleanup;
+ }
+ cJSON_Delete(old_item);
+ if (opcode == REMOVE)
+ {
+ /* For Remove, this job is done. */
+ status = 0;
+ goto cleanup;
+ }
+ }
+
+ /* Copy/Move uses "from". */
+ if ((opcode == MOVE) || (opcode == COPY))
+ {
+ cJSON *from = get_object_item(patch, "from", case_sensitive);
+ if (from == NULL)
+ {
+ /* missing "from" for copy/move. */
+ status = 4;
+ goto cleanup;
+ }
+
+ if (opcode == MOVE)
+ {
+ value = detach_path(object, (unsigned char*)from->valuestring, case_sensitive);
+ }
+ if (opcode == COPY)
+ {
+ value = get_item_from_pointer(object, from->valuestring, case_sensitive);
+ }
+ if (value == NULL)
+ {
+ /* missing "from" for copy/move. */
+ status = 5;
+ goto cleanup;
+ }
+ if (opcode == COPY)
+ {
+ value = cJSON_Duplicate(value, 1);
+ }
+ if (value == NULL)
+ {
+ /* out of memory for copy/move. */
+ status = 6;
+ goto cleanup;
+ }
+ }
+ else /* Add/Replace uses "value". */
+ {
+ value = get_object_item(patch, "value", case_sensitive);
+ if (value == NULL)
+ {
+ /* missing "value" for add/replace. */
+ status = 7;
+ goto cleanup;
+ }
+ value = cJSON_Duplicate(value, 1);
+ if (value == NULL)
+ {
+ /* out of memory for add/replace. */
+ status = 8;
+ goto cleanup;
+ }
+ }
+
+ /* Now, just add "value" to "path". */
+
+ /* split pointer in parent and child */
+ parent_pointer = cJSONUtils_strdup((unsigned char*)path->valuestring);
+ if (parent_pointer) {
+ child_pointer = (unsigned char*)strrchr((char*)parent_pointer, '/');
+ }
+ if (child_pointer != NULL)
+ {
+ child_pointer[0] = '\0';
+ child_pointer++;
+ }
+ parent = get_item_from_pointer(object, (char*)parent_pointer, case_sensitive);
+ decode_pointer_inplace(child_pointer);
+
+ /* add, remove, replace, move, copy, test. */
+ if ((parent == NULL) || (child_pointer == NULL))
+ {
+ /* Couldn't find object to add to. */
+ status = 9;
+ goto cleanup;
+ }
+ else if (cJSON_IsArray(parent))
+ {
+ if (strcmp((char*)child_pointer, "-") == 0)
+ {
+ cJSON_AddItemToArray(parent, value);
+ value = NULL;
+ }
+ else
+ {
+ size_t index = 0;
+ if (!decode_array_index_from_pointer(child_pointer, &index))
+ {
+ status = 11;
+ goto cleanup;
+ }
+
+ if (!insert_item_in_array(parent, index, value))
+ {
+ status = 10;
+ goto cleanup;
+ }
+ value = NULL;
+ }
+ }
+ else if (cJSON_IsObject(parent))
+ {
+ if (case_sensitive)
+ {
+ cJSON_DeleteItemFromObjectCaseSensitive(parent, (char*)child_pointer);
+ }
+ else
+ {
+ cJSON_DeleteItemFromObject(parent, (char*)child_pointer);
+ }
+ cJSON_AddItemToObject(parent, (char*)child_pointer, value);
+ value = NULL;
+ }
+ else /* parent is not an object */
+ {
+ /* Couldn't find object to add to. */
+ status = 9;
+ goto cleanup;
+ }
+
+cleanup:
+ if (value != NULL)
+ {
+ cJSON_Delete(value);
+ }
+ if (parent_pointer != NULL)
+ {
+ cJSON_free(parent_pointer);
+ }
+
+ return status;
+}
+
+CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON * const object, const cJSON * const patches)
+{
+ const cJSON *current_patch = NULL;
+ int status = 0;
+
+ if (!cJSON_IsArray(patches))
+ {
+ /* malformed patches. */
+ return 1;
+ }
+
+ if (patches != NULL)
+ {
+ current_patch = patches->child;
+ }
+
+ while (current_patch != NULL)
+ {
+ status = apply_patch(object, current_patch, false);
+ if (status != 0)
+ {
+ return status;
+ }
+ current_patch = current_patch->next;
+ }
+
+ return 0;
+}
+
+CJSON_PUBLIC(int) cJSONUtils_ApplyPatchesCaseSensitive(cJSON * const object, const cJSON * const patches)
+{
+ const cJSON *current_patch = NULL;
+ int status = 0;
+
+ if (!cJSON_IsArray(patches))
+ {
+ /* malformed patches. */
+ return 1;
+ }
+
+ if (patches != NULL)
+ {
+ current_patch = patches->child;
+ }
+
+ while (current_patch != NULL)
+ {
+ status = apply_patch(object, current_patch, true);
+ if (status != 0)
+ {
+ return status;
+ }
+ current_patch = current_patch->next;
+ }
+
+ return 0;
+}
+
+static void compose_patch(cJSON * const patches, const unsigned char * const operation, const unsigned char * const path, const unsigned char *suffix, const cJSON * const value)
+{
+ cJSON *patch = NULL;
+
+ if ((patches == NULL) || (operation == NULL) || (path == NULL))
+ {
+ return;
+ }
+
+ patch = cJSON_CreateObject();
+ if (patch == NULL)
+ {
+ return;
+ }
+ cJSON_AddItemToObject(patch, "op", cJSON_CreateString((const char*)operation));
+
+ if (suffix == NULL)
+ {
+ cJSON_AddItemToObject(patch, "path", cJSON_CreateString((const char*)path));
+ }
+ else
+ {
+ size_t suffix_length = pointer_encoded_length(suffix);
+ size_t path_length = strlen((const char*)path);
+ unsigned char *full_path = (unsigned char*)cJSON_malloc(path_length + suffix_length + sizeof("/"));
+
+ sprintf((char*)full_path, "%s/", (const char*)path);
+ encode_string_as_pointer(full_path + path_length + 1, suffix);
+
+ cJSON_AddItemToObject(patch, "path", cJSON_CreateString((const char*)full_path));
+ cJSON_free(full_path);
+ }
+
+ if (value != NULL)
+ {
+ cJSON_AddItemToObject(patch, "value", cJSON_Duplicate(value, 1));
+ }
+ cJSON_AddItemToArray(patches, patch);
+}
+
+CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON * const array, const char * const operation, const char * const path, const cJSON * const value)
+{
+ compose_patch(array, (const unsigned char*)operation, (const unsigned char*)path, NULL, value);
+}
+
+static void create_patches(cJSON * const patches, const unsigned char * const path, cJSON * const from, cJSON * const to, const cJSON_bool case_sensitive)
+{
+ if ((from == NULL) || (to == NULL))
+ {
+ return;
+ }
+
+ if ((from->type & 0xFF) != (to->type & 0xFF))
+ {
+ compose_patch(patches, (const unsigned char*)"replace", path, 0, to);
+ return;
+ }
+
+ switch (from->type & 0xFF)
+ {
+ case cJSON_Number:
+ if ((from->valueint != to->valueint) || !compare_double(from->valuedouble, to->valuedouble))
+ {
+ compose_patch(patches, (const unsigned char*)"replace", path, NULL, to);
+ }
+ return;
+
+ case cJSON_String:
+ if (strcmp(from->valuestring, to->valuestring) != 0)
+ {
+ compose_patch(patches, (const unsigned char*)"replace", path, NULL, to);
+ }
+ return;
+
+ case cJSON_Array:
+ {
+ size_t index = 0;
+ cJSON *from_child = from->child;
+ cJSON *to_child = to->child;
+ unsigned char *new_path = (unsigned char*)cJSON_malloc(strlen((const char*)path) + 20 + sizeof("/")); /* Allow space for 64bit int. log10(2^64) = 20 */
+
+ /* generate patches for all array elements that exist in both "from" and "to" */
+ for (index = 0; (from_child != NULL) && (to_child != NULL); (void)(from_child = from_child->next), (void)(to_child = to_child->next), index++)
+ {
+ /* check if conversion to unsigned long is valid
+ * This should be eliminated at compile time by dead code elimination
+ * if size_t is an alias of unsigned long, or if it is bigger */
+ if (index > ULONG_MAX)
+ {
+ cJSON_free(new_path);
+ return;
+ }
+ sprintf((char*)new_path, "%s/%lu", path, (unsigned long)index); /* path of the current array element */
+ create_patches(patches, new_path, from_child, to_child, case_sensitive);
+ }
+
+ /* remove leftover elements from 'from' that are not in 'to' */
+ for (; (from_child != NULL); (void)(from_child = from_child->next))
+ {
+ /* check if conversion to unsigned long is valid
+ * This should be eliminated at compile time by dead code elimination
+ * if size_t is an alias of unsigned long, or if it is bigger */
+ if (index > ULONG_MAX)
+ {
+ cJSON_free(new_path);
+ return;
+ }
+ sprintf((char*)new_path, "%lu", (unsigned long)index);
+ compose_patch(patches, (const unsigned char*)"remove", path, new_path, NULL);
+ }
+ /* add new elements in 'to' that were not in 'from' */
+ for (; (to_child != NULL); (void)(to_child = to_child->next), index++)
+ {
+ compose_patch(patches, (const unsigned char*)"add", path, (const unsigned char*)"-", to_child);
+ }
+ cJSON_free(new_path);
+ return;
+ }
+
+ case cJSON_Object:
+ {
+ cJSON *from_child = NULL;
+ cJSON *to_child = NULL;
+ sort_object(from, case_sensitive);
+ sort_object(to, case_sensitive);
+
+ from_child = from->child;
+ to_child = to->child;
+ /* for all object values in the object with more of them */
+ while ((from_child != NULL) || (to_child != NULL))
+ {
+ int diff;
+ if (from_child == NULL)
+ {
+ diff = 1;
+ }
+ else if (to_child == NULL)
+ {
+ diff = -1;
+ }
+ else
+ {
+ diff = compare_strings((unsigned char*)from_child->string, (unsigned char*)to_child->string, case_sensitive);
+ }
+
+ if (diff == 0)
+ {
+ /* both object keys are the same */
+ size_t path_length = strlen((const char*)path);
+ size_t from_child_name_length = pointer_encoded_length((unsigned char*)from_child->string);
+ unsigned char *new_path = (unsigned char*)cJSON_malloc(path_length + from_child_name_length + sizeof("/"));
+
+ sprintf((char*)new_path, "%s/", path);
+ encode_string_as_pointer(new_path + path_length + 1, (unsigned char*)from_child->string);
+
+ /* create a patch for the element */
+ create_patches(patches, new_path, from_child, to_child, case_sensitive);
+ cJSON_free(new_path);
+
+ from_child = from_child->next;
+ to_child = to_child->next;
+ }
+ else if (diff < 0)
+ {
+ /* object element doesn't exist in 'to' --> remove it */
+ compose_patch(patches, (const unsigned char*)"remove", path, (unsigned char*)from_child->string, NULL);
+
+ from_child = from_child->next;
+ }
+ else
+ {
+ /* object element doesn't exist in 'from' --> add it */
+ compose_patch(patches, (const unsigned char*)"add", path, (unsigned char*)to_child->string, to_child);
+
+ to_child = to_child->next;
+ }
+ }
+ return;
+ }
+
+ default:
+ break;
+ }
+}
+
+CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON * const from, cJSON * const to)
+{
+ cJSON *patches = NULL;
+
+ if ((from == NULL) || (to == NULL))
+ {
+ return NULL;
+ }
+
+ patches = cJSON_CreateArray();
+ create_patches(patches, (const unsigned char*)"", from, to, false);
+
+ return patches;
+}
+
+CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatchesCaseSensitive(cJSON * const from, cJSON * const to)
+{
+ cJSON *patches = NULL;
+
+ if ((from == NULL) || (to == NULL))
+ {
+ return NULL;
+ }
+
+ patches = cJSON_CreateArray();
+ create_patches(patches, (const unsigned char*)"", from, to, true);
+
+ return patches;
+}
+
+CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON * const object)
+{
+ sort_object(object, false);
+}
+
+CJSON_PUBLIC(void) cJSONUtils_SortObjectCaseSensitive(cJSON * const object)
+{
+ sort_object(object, true);
+}
+
+static cJSON *merge_patch(cJSON *target, const cJSON * const patch, const cJSON_bool case_sensitive)
+{
+ cJSON *patch_child = NULL;
+
+ if (!cJSON_IsObject(patch))
+ {
+ /* scalar value, array or NULL, just duplicate */
+ cJSON_Delete(target);
+ return cJSON_Duplicate(patch, 1);
+ }
+
+ if (!cJSON_IsObject(target))
+ {
+ cJSON_Delete(target);
+ target = cJSON_CreateObject();
+ }
+
+ patch_child = patch->child;
+ while (patch_child != NULL)
+ {
+ if (cJSON_IsNull(patch_child))
+ {
+ /* NULL is the indicator to remove a value, see RFC7396 */
+ if (case_sensitive)
+ {
+ cJSON_DeleteItemFromObjectCaseSensitive(target, patch_child->string);
+ }
+ else
+ {
+ cJSON_DeleteItemFromObject(target, patch_child->string);
+ }
+ }
+ else
+ {
+ cJSON *replace_me = NULL;
+ cJSON *replacement = NULL;
+
+ if (case_sensitive)
+ {
+ replace_me = cJSON_DetachItemFromObjectCaseSensitive(target, patch_child->string);
+ }
+ else
+ {
+ replace_me = cJSON_DetachItemFromObject(target, patch_child->string);
+ }
+
+ replacement = merge_patch(replace_me, patch_child, case_sensitive);
+ if (replacement == NULL)
+ {
+ cJSON_Delete(target);
+ return NULL;
+ }
+
+ cJSON_AddItemToObject(target, patch_child->string, replacement);
+ }
+ patch_child = patch_child->next;
+ }
+ return target;
+}
+
+CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, const cJSON * const patch)
+{
+ return merge_patch(target, patch, false);
+}
+
+CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatchCaseSensitive(cJSON *target, const cJSON * const patch)
+{
+ return merge_patch(target, patch, true);
+}
+
+static cJSON *generate_merge_patch(cJSON * const from, cJSON * const to, const cJSON_bool case_sensitive)
+{
+ cJSON *from_child = NULL;
+ cJSON *to_child = NULL;
+ cJSON *patch = NULL;
+ if (to == NULL)
+ {
+ /* patch to delete everything */
+ return cJSON_CreateNull();
+ }
+ if (!cJSON_IsObject(to) || !cJSON_IsObject(from))
+ {
+ return cJSON_Duplicate(to, 1);
+ }
+
+ sort_object(from, case_sensitive);
+ sort_object(to, case_sensitive);
+
+ from_child = from->child;
+ to_child = to->child;
+ patch = cJSON_CreateObject();
+ if (patch == NULL)
+ {
+ return NULL;
+ }
+ while (from_child || to_child)
+ {
+ int diff;
+ if (from_child != NULL)
+ {
+ if (to_child != NULL)
+ {
+ diff = strcmp(from_child->string, to_child->string);
+ }
+ else
+ {
+ diff = -1;
+ }
+ }
+ else
+ {
+ diff = 1;
+ }
+
+ if (diff < 0)
+ {
+ /* from has a value that to doesn't have -> remove */
+ cJSON_AddItemToObject(patch, from_child->string, cJSON_CreateNull());
+
+ from_child = from_child->next;
+ }
+ else if (diff > 0)
+ {
+ /* to has a value that from doesn't have -> add to patch */
+ cJSON_AddItemToObject(patch, to_child->string, cJSON_Duplicate(to_child, 1));
+
+ to_child = to_child->next;
+ }
+ else
+ {
+ /* object key exists in both objects */
+ if (!compare_json(from_child, to_child, case_sensitive))
+ {
+ /* not identical --> generate a patch */
+ cJSON_AddItemToObject(patch, to_child->string, cJSONUtils_GenerateMergePatch(from_child, to_child));
+ }
+
+ /* next key in the object */
+ from_child = from_child->next;
+ to_child = to_child->next;
+ }
+ }
+ if (patch->child == NULL)
+ {
+ /* no patch generated */
+ cJSON_Delete(patch);
+ return NULL;
+ }
+
+ return patch;
+}
+
+CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON * const from, cJSON * const to)
+{
+ return generate_merge_patch(from, to, false);
+}
+
+CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatchCaseSensitive(cJSON * const from, cJSON * const to)
+{
+ return generate_merge_patch(from, to, true);
+}
diff --git a/packages/cJSON-v1.7.17/cJSON_Utils.h b/packages/cJSON-v1.7.17/cJSON_Utils.h
new file mode 100644
index 0000000..a970c65
--- /dev/null
+++ b/packages/cJSON-v1.7.17/cJSON_Utils.h
@@ -0,0 +1,88 @@
+/*
+ Copyright (c) 2009-2017 Dave Gamble and cJSON contributors
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+#ifndef cJSON_Utils__h
+#define cJSON_Utils__h
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include "cJSON.h"
+
+/* Implement RFC6901 (https://tools.ietf.org/html/rfc6901) JSON Pointer spec. */
+CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointer(cJSON * const object, const char *pointer);
+CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointerCaseSensitive(cJSON * const object, const char *pointer);
+
+/* Implement RFC6902 (https://tools.ietf.org/html/rfc6902) JSON Patch spec. */
+/* NOTE: This modifies objects in 'from' and 'to' by sorting the elements by their key */
+CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON * const from, cJSON * const to);
+CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatchesCaseSensitive(cJSON * const from, cJSON * const to);
+/* Utility for generating patch array entries. */
+CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON * const array, const char * const operation, const char * const path, const cJSON * const value);
+/* Returns 0 for success. */
+CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON * const object, const cJSON * const patches);
+CJSON_PUBLIC(int) cJSONUtils_ApplyPatchesCaseSensitive(cJSON * const object, const cJSON * const patches);
+
+/*
+// Note that ApplyPatches is NOT atomic on failure. To implement an atomic ApplyPatches, use:
+//int cJSONUtils_AtomicApplyPatches(cJSON **object, cJSON *patches)
+//{
+// cJSON *modme = cJSON_Duplicate(*object, 1);
+// int error = cJSONUtils_ApplyPatches(modme, patches);
+// if (!error)
+// {
+// cJSON_Delete(*object);
+// *object = modme;
+// }
+// else
+// {
+// cJSON_Delete(modme);
+// }
+//
+// return error;
+//}
+// Code not added to library since this strategy is a LOT slower.
+*/
+
+/* 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. */
+CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, const cJSON * const patch);
+CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatchCaseSensitive(cJSON *target, const cJSON * const patch);
+/* generates a patch to move from -> to */
+/* NOTE: This modifies objects in 'from' and 'to' by sorting the elements by their key */
+CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON * const from, cJSON * const to);
+CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatchCaseSensitive(cJSON * const from, cJSON * const to);
+
+/* Given a root object and a target object, construct a pointer from one to the other. */
+CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(const cJSON * const object, const cJSON * const target);
+
+/* Sorts the members of the object into alphabetical order. */
+CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON * const object);
+CJSON_PUBLIC(void) cJSONUtils_SortObjectCaseSensitive(cJSON * const object);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/packages/cJSON-v1.7.17/cJSON_util.h b/packages/cJSON-v1.7.17/cJSON_util.h
new file mode 100644
index 0000000..1855326
--- /dev/null
+++ b/packages/cJSON-v1.7.17/cJSON_util.h
@@ -0,0 +1 @@
+#include "cJSON_Utils.h"
diff --git a/packages/packages.dbsqlite b/packages/packages.dbsqlite
index 0592e4e..5d3ff58 100644
Binary files a/packages/packages.dbsqlite and b/packages/packages.dbsqlite differ
diff --git a/packages/pkgs.json b/packages/pkgs.json
index 0637a08..d0e9680 100644
--- a/packages/pkgs.json
+++ b/packages/pkgs.json
@@ -1 +1,12 @@
-[]
\ No newline at end of file
+[
+ {
+ "path": "/packages/language/JSON/cJSON",
+ "ver": "v1.7.17",
+ "name": "CJSON"
+ },
+ {
+ "path": "/packages/system/sqlite",
+ "ver": "v3.19.3",
+ "name": "SQLITE"
+ }
+]
\ No newline at end of file
diff --git a/packages/sqlite-v3.19.3 b/packages/sqlite-v3.19.3
new file mode 160000
index 0000000..1674d90
--- /dev/null
+++ b/packages/sqlite-v3.19.3
@@ -0,0 +1 @@
+Subproject commit 1674d90269da465401e71a11947ab70b3c7f17b0
diff --git a/rt-thread/components/dfs/dfs_v1/include/dfs_poll.h b/rt-thread/components/dfs/dfs_v1/include/dfs_poll.h
new file mode 100644
index 0000000..a59e7e2
--- /dev/null
+++ b/rt-thread/components/dfs/dfs_v1/include/dfs_poll.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021-11-14 Meco Man the first version
+ */
+
+#ifndef DFS_POLL_H__
+#define DFS_POLL_H__
+
+#include
+
+#endif /* DFS_POLL_H__ */
diff --git a/rt-thread/components/dfs/dfs_v1/include/dfs_posix.h b/rt-thread/components/dfs/dfs_v1/include/dfs_posix.h
new file mode 100644
index 0000000..ee62cdd
--- /dev/null
+++ b/rt-thread/components/dfs/dfs_v1/include/dfs_posix.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2009-05-27 Yi.qiu The first version.
+ * 2010-07-18 Bernard add stat and statfs structure definitions.
+ * 2011-05-16 Yi.qiu Change parameter name of rename, "new" is C++ key word.
+ * 2017-12-27 Bernard Add fcntl API.
+ * 2018-02-07 Bernard Change the 3rd parameter of open/fcntl/ioctl to '...'
+ */
+
+#ifndef __DFS_POSIX_H__
+#define __DFS_POSIX_H__
+
+#include
+#include
+#include /* rename() */
+#include
+#include /* statfs() */
+
+#endif
diff --git a/rt-thread/components/dfs/dfs_v1/include/dfs_select.h b/rt-thread/components/dfs/dfs_v1/include/dfs_select.h
new file mode 100644
index 0000000..e7cf190
--- /dev/null
+++ b/rt-thread/components/dfs/dfs_v1/include/dfs_select.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021-11-14 Meco Man the first version
+ */
+
+#ifndef DFS_SELECT_H__
+#define DFS_SELECT_H__
+
+#include
+
+#endif /* DFS_SELECT_H__ */
diff --git a/rt-thread/components/drivers/include/drivers/pin.h b/rt-thread/components/drivers/include/drivers/pin.h
index 22d4328..8df72d6 100644
--- a/rt-thread/components/drivers/include/drivers/pin.h
+++ b/rt-thread/components/drivers/include/drivers/pin.h
@@ -126,13 +126,13 @@ struct rt_pin_ctrl_conf_params
struct rt_pin_ops
{
- void (*pin_mode)(struct rt_device *device, rt_base_t pin, rt_uint8_t mode);
- void (*pin_write)(struct rt_device *device, rt_base_t pin, rt_uint8_t value);
+ void (*pin_mode)(struct rt_device *device, rt_base_t pin, rt_base_t mode);
+ void (*pin_write)(struct rt_device *device, rt_base_t pin, rt_base_t value);
rt_ssize_t (*pin_read)(struct rt_device *device, rt_base_t pin);
rt_err_t (*pin_attach_irq)(struct rt_device *device, rt_base_t pin,
- rt_uint8_t mode, void (*hdr)(void *args), void *args);
+ rt_uint32_t mode, void (*hdr)(void *args), void *args);
rt_err_t (*pin_detach_irq)(struct rt_device *device, rt_base_t pin);
- rt_err_t (*pin_irq_enable)(struct rt_device *device, rt_base_t pin, rt_uint8_t enabled);
+ rt_err_t (*pin_irq_enable)(struct rt_device *device, rt_base_t pin, rt_uint32_t enabled);
rt_base_t (*pin_get)(const char *name);
#ifdef RT_USING_DM
rt_err_t (*pin_irq_mode)(struct rt_device *device, rt_base_t pin, rt_uint8_t mode);
diff --git a/rt-thread/components/libc/compilers/common/include/sys/time.h b/rt-thread/components/libc/compilers/common/include/sys/time.h
index 817fe78..032e459 100644
--- a/rt-thread/components/libc/compilers/common/include/sys/time.h
+++ b/rt-thread/components/libc/compilers/common/include/sys/time.h
@@ -16,6 +16,12 @@
#include
#include
#include
+
+typedef _TIME_T_ time_t;
+typedef long suseconds_t; /* microseconds. */
+typedef unsigned long useconds_t; /* microseconds (unsigned) */
+typedef __clockid_t clockid_t;
+
#ifdef _WIN32
typedef __time64_t time_t;
#endif /* _WIN32 */
diff --git a/rt-thread/src/kservice.c b/rt-thread/src/kservice.c
index 7c0cb0b..427988a 100644
--- a/rt-thread/src/kservice.c
+++ b/rt-thread/src/kservice.c
@@ -244,17 +244,17 @@ RTM_EXPORT(_rt_errno);
*/
void rt_show_version(void)
{
- rt_kprintf("\n \\ | /\n");
+ rt_kprintf("\n \\ | / SUNLGHT SC828F b0.0.1\n");
#if defined(RT_USING_SMART)
rt_kprintf("- RT - Thread Smart Operating System\n");
#elif defined(RT_USING_NANO)
rt_kprintf("- RT - Thread Nano Operating System\n");
#else
- rt_kprintf("- RT - Thread Operating System\n");
+ rt_kprintf("- SC - Thread Operating System\n");
#endif
rt_kprintf(" / | \\ %d.%d.%d build %s %s\n",
(rt_int32_t)RT_VERSION_MAJOR, (rt_int32_t)RT_VERSION_MINOR, (rt_int32_t)RT_VERSION_PATCH, __DATE__, __TIME__);
- rt_kprintf(" 2006 - 2024 Copyright by RT-Thread team\n");
+ //rt_kprintf(" 2006 - 2024 Copyright by RT-Thread team\n");
}
RTM_EXPORT(rt_show_version);
diff --git a/rtconfig.h b/rtconfig.h
index f55fdca..ec8715f 100644
--- a/rtconfig.h
+++ b/rtconfig.h
@@ -98,15 +98,19 @@
#define RT_DFS_ELM_WORD_ACCESS
#define RT_DFS_ELM_USE_LFN_3
#define RT_DFS_ELM_USE_LFN 3
-#define RT_DFS_ELM_LFN_UNICODE_0
-#define RT_DFS_ELM_LFN_UNICODE 0
+#define RT_DFS_ELM_LFN_UNICODE_2
+#define RT_DFS_ELM_LFN_UNICODE 2
#define RT_DFS_ELM_MAX_LFN 255
#define RT_DFS_ELM_DRIVES 2
#define RT_DFS_ELM_MAX_SECTOR_SIZE 512
+#define RT_DFS_ELM_USE_ERASE
#define RT_DFS_ELM_REENTRANT
#define RT_DFS_ELM_MUTEX_TIMEOUT 3000
+#define RT_DFS_ELM_USE_EXFAT
/* end of elm-chan's FatFs, Generic FAT Filesystem Module */
#define RT_USING_DFS_DEVFS
+#define RT_USING_DFS_TMPFS
+#define RT_USING_DFS_MQUEUE
/* end of DFS: device virtual file system */
/* Device Drivers */
@@ -115,7 +119,9 @@
#define RT_UNAMED_PIPE_NUMBER 64
#define RT_USING_SERIAL
#define RT_USING_SERIAL_V1
-#define RT_SERIAL_RB_BUFSZ 64
+#define RT_SERIAL_RB_BUFSZ 512
+#define RT_USING_RTC
+#define RT_USING_SOFT_RTC
#define RT_USING_SDIO
#define RT_SDIO_STACK_SIZE 512
#define RT_SDIO_THREAD_PRIORITY 15
@@ -125,6 +131,7 @@
#define RT_USING_SPI
#define RT_USING_QSPI
#define RT_USING_SPI_MSD
+#define RT_USING_DEV_BUS
#define RT_USING_PIN
/* Using USB */
@@ -212,6 +219,8 @@
/* JSON: JavaScript Object Notation, a lightweight data-interchange format */
+#define PKG_USING_CJSON
+#define PKG_USING_CJSON_V1717
/* end of JSON: JavaScript Object Notation, a lightweight data-interchange format */
/* XML: Extensible Markup Language */
@@ -251,6 +260,10 @@
/* Micrium: Micrium software products porting for RT-Thread */
/* end of Micrium: Micrium software products porting for RT-Thread */
+#define PKG_USING_SQLITE
+#define PKG_SQLITE_SQL_MAX_LEN 1024
+#define PKG_SQLITE_DB_NAME_MAX_LEN 64
+#define PKG_USING_SQLITE_V3193
/* end of system packages */
/* peripheral libraries and drivers */