diff --git a/package/jrpc/jrpc.c b/package/jrpc/jrpc.c index 18a7dc78c..8656cc568 100644 --- a/package/jrpc/jrpc.c +++ b/package/jrpc/jrpc.c @@ -261,7 +261,8 @@ void JRPC_server_handle_string(JRPC* self, char* json_str) { jrpc_free(ack_str); } - cJSON** param_array = (cJSON**)jrpc_malloc(param_count * sizeof(cJSON*)); + cJSON** param_array = + (cJSON**)jrpc_malloc(param_count * sizeof(cJSON*) + 1); if (param_array == NULL) { jrpc_debug("Memory allocation failed for param_array\n"); JRPC_send_acknowledgement(self, id->valueint, ACK_MEMORY_ERROR, @@ -726,17 +727,23 @@ int jrpc_test_server() { char* jrpc_strtok(char* str, const char* delimiters, char** context) { char* start = str ? str : *context; - if (!start) + if (!start) { return NULL; + } - while (*start && strchr(delimiters, *start)) + // Skip initial delimiters + while (*start && strchr(delimiters, *start)) { ++start; - if (!*start) + } + if (!*start) { + *context = NULL; return NULL; + } char* end = start; - while (*end && !strchr(delimiters, *end)) + while (*end && !strchr(delimiters, *end)) { ++end; + } if (*end) { *end = '\0'; @@ -748,6 +755,36 @@ char* jrpc_strtok(char* str, const char* delimiters, char** context) { return start; } +static char* extract_quoted_string(const char** input) { + const char* start = *input; + if (*start != '"') { + return NULL; + } + start++; + const char* end = strchr(start, '"'); + if (!end) { + return NULL; + } + size_t len = end - start; + char* result = (char*)malloc(len + 1); + if (!result) { + return NULL; + } + strncpy(result, start, len); + result[len] = '\0'; + *input = end + 1; + return result; +} + +static void skip_whitespace(const char** input) { + if (input == NULL || *input == NULL) { + return; + } + while (**input == ' ' || **input == '\t' || **input == '\n') { + (*input)++; + } +} + char* JRPC_cmd(JRPC* jrpc, const char* cmd) { char* cmd_copy = NULL; char* method = NULL; @@ -762,8 +799,10 @@ char* JRPC_cmd(JRPC* jrpc, const char* cmd) { goto __exit; } - char* context = NULL; - char* token = jrpc_strtok(cmd_copy, " ", &context); + const char* cursor = cmd_copy; + skip_whitespace(&cursor); + + char* token = jrpc_strtok(cmd_copy, " ", (char**)&cursor); if (token == NULL) { jrpc_debug("Invalid command\n"); goto __exit; @@ -775,18 +814,52 @@ char* JRPC_cmd(JRPC* jrpc, const char* cmd) { goto __exit; } - while ((token = jrpc_strtok(NULL, " ", &context)) != NULL) { - int param_value = atoi(token); - params_array[param_count] = cJSON_CreateNumber(param_value); - if (!params_array[param_count]) { - jrpc_debug("Failed to create JSON number\n"); + skip_whitespace(&cursor); + + while (cursor && *cursor != '\0') { + cJSON* param = NULL; + if (*cursor == '"') { + char* str_param = extract_quoted_string(&cursor); + if (!str_param) { + jrpc_debug("Failed to extract quoted string\n"); + goto __exit; + } + param = cJSON_CreateString(str_param); + free(str_param); + } else { + const char* start = cursor; + while (*cursor != ' ' && *cursor != '\0') { + cursor++; + } + size_t len = cursor - start; + char* token_param = (char*)malloc(len + 1); + if (!token_param) { + jrpc_debug("Failed to allocate memory for token_param\n"); + goto __exit; + } + strncpy(token_param, start, len); + token_param[len] = '\0'; + + param = cJSON_Parse(token_param); + if (!param) { + param = cJSON_CreateString(token_param); + } + free(token_param); + } + if (!param) { + jrpc_debug("Failed to create JSON parameter\n"); goto __exit; } + params_array[param_count] = param; param_count++; + + skip_whitespace(&cursor); } - result = - JRPC_send_request_blocking(jrpc, method, params_array, param_count); + // Ensure params_array and param_count are handled correctly when + // param_count is 0 + result = JRPC_send_request_blocking( + jrpc, method, param_count > 0 ? params_array : NULL, param_count); if (result == NULL) { jrpc_debug("No result\n"); goto __exit; diff --git a/package/jrpc/jrpc.h b/package/jrpc/jrpc.h index 6cecf43e6..56aae4347 100644 --- a/package/jrpc/jrpc.h +++ b/package/jrpc/jrpc.h @@ -27,7 +27,7 @@ extern "C" { #define PARAM_COUNT_NO_CHECK -1 // Timeout definitions -#define ACK_TIMEOUT 50 +#define ACK_TIMEOUT 200 #define BLOCKING_TIMEOUT 1000 #define RETRY_COUNT 5 diff --git a/port/linux/.vscode/launch.json b/port/linux/.vscode/launch.json index 792b34dd4..615a29463 100644 --- a/port/linux/.vscode/launch.json +++ b/port/linux/.vscode/launch.json @@ -28,10 +28,11 @@ // "--gtest_filter=time*" // "--gtest_filter=flashdb.tsdb1" // "--gtest_filter=flashdb.base" - "--gtest_filter=jrpc.server" + // "--gtest_filter=jrpc.server" // "--gtest_filter=jrpc.client" // "--gtest_filter=jrpc.BlockingRequestBetweenTwoJRPC" // "--gtest_filter=jrpc.cmd" + "--gtest_filter=jrpc.exec_get_val" ], "stopAtEntry": false, "cwd": "${workspaceFolder}", diff --git a/port/linux/package/pikascript/pikascript-lib/jrpc/jrpc.c b/port/linux/package/pikascript/pikascript-lib/jrpc/jrpc.c index 18a7dc78c..8656cc568 100644 --- a/port/linux/package/pikascript/pikascript-lib/jrpc/jrpc.c +++ b/port/linux/package/pikascript/pikascript-lib/jrpc/jrpc.c @@ -261,7 +261,8 @@ void JRPC_server_handle_string(JRPC* self, char* json_str) { jrpc_free(ack_str); } - cJSON** param_array = (cJSON**)jrpc_malloc(param_count * sizeof(cJSON*)); + cJSON** param_array = + (cJSON**)jrpc_malloc(param_count * sizeof(cJSON*) + 1); if (param_array == NULL) { jrpc_debug("Memory allocation failed for param_array\n"); JRPC_send_acknowledgement(self, id->valueint, ACK_MEMORY_ERROR, @@ -726,17 +727,23 @@ int jrpc_test_server() { char* jrpc_strtok(char* str, const char* delimiters, char** context) { char* start = str ? str : *context; - if (!start) + if (!start) { return NULL; + } - while (*start && strchr(delimiters, *start)) + // Skip initial delimiters + while (*start && strchr(delimiters, *start)) { ++start; - if (!*start) + } + if (!*start) { + *context = NULL; return NULL; + } char* end = start; - while (*end && !strchr(delimiters, *end)) + while (*end && !strchr(delimiters, *end)) { ++end; + } if (*end) { *end = '\0'; @@ -748,6 +755,36 @@ char* jrpc_strtok(char* str, const char* delimiters, char** context) { return start; } +static char* extract_quoted_string(const char** input) { + const char* start = *input; + if (*start != '"') { + return NULL; + } + start++; + const char* end = strchr(start, '"'); + if (!end) { + return NULL; + } + size_t len = end - start; + char* result = (char*)malloc(len + 1); + if (!result) { + return NULL; + } + strncpy(result, start, len); + result[len] = '\0'; + *input = end + 1; + return result; +} + +static void skip_whitespace(const char** input) { + if (input == NULL || *input == NULL) { + return; + } + while (**input == ' ' || **input == '\t' || **input == '\n') { + (*input)++; + } +} + char* JRPC_cmd(JRPC* jrpc, const char* cmd) { char* cmd_copy = NULL; char* method = NULL; @@ -762,8 +799,10 @@ char* JRPC_cmd(JRPC* jrpc, const char* cmd) { goto __exit; } - char* context = NULL; - char* token = jrpc_strtok(cmd_copy, " ", &context); + const char* cursor = cmd_copy; + skip_whitespace(&cursor); + + char* token = jrpc_strtok(cmd_copy, " ", (char**)&cursor); if (token == NULL) { jrpc_debug("Invalid command\n"); goto __exit; @@ -775,18 +814,52 @@ char* JRPC_cmd(JRPC* jrpc, const char* cmd) { goto __exit; } - while ((token = jrpc_strtok(NULL, " ", &context)) != NULL) { - int param_value = atoi(token); - params_array[param_count] = cJSON_CreateNumber(param_value); - if (!params_array[param_count]) { - jrpc_debug("Failed to create JSON number\n"); + skip_whitespace(&cursor); + + while (cursor && *cursor != '\0') { + cJSON* param = NULL; + if (*cursor == '"') { + char* str_param = extract_quoted_string(&cursor); + if (!str_param) { + jrpc_debug("Failed to extract quoted string\n"); + goto __exit; + } + param = cJSON_CreateString(str_param); + free(str_param); + } else { + const char* start = cursor; + while (*cursor != ' ' && *cursor != '\0') { + cursor++; + } + size_t len = cursor - start; + char* token_param = (char*)malloc(len + 1); + if (!token_param) { + jrpc_debug("Failed to allocate memory for token_param\n"); + goto __exit; + } + strncpy(token_param, start, len); + token_param[len] = '\0'; + + param = cJSON_Parse(token_param); + if (!param) { + param = cJSON_CreateString(token_param); + } + free(token_param); + } + if (!param) { + jrpc_debug("Failed to create JSON parameter\n"); goto __exit; } + params_array[param_count] = param; param_count++; + + skip_whitespace(&cursor); } - result = - JRPC_send_request_blocking(jrpc, method, params_array, param_count); + // Ensure params_array and param_count are handled correctly when + // param_count is 0 + result = JRPC_send_request_blocking( + jrpc, method, param_count > 0 ? params_array : NULL, param_count); if (result == NULL) { jrpc_debug("No result\n"); goto __exit; diff --git a/port/linux/package/pikascript/pikascript-lib/jrpc/jrpc.h b/port/linux/package/pikascript/pikascript-lib/jrpc/jrpc.h index 6cecf43e6..56aae4347 100644 --- a/port/linux/package/pikascript/pikascript-lib/jrpc/jrpc.h +++ b/port/linux/package/pikascript/pikascript-lib/jrpc/jrpc.h @@ -27,7 +27,7 @@ extern "C" { #define PARAM_COUNT_NO_CHECK -1 // Timeout definitions -#define ACK_TIMEOUT 50 +#define ACK_TIMEOUT 200 #define BLOCKING_TIMEOUT 1000 #define RETRY_COUNT 5 diff --git a/port/linux/test/module-test.cpp b/port/linux/test/module-test.cpp index 41ef316d6..e4d5ff361 100644 --- a/port/linux/test/module-test.cpp +++ b/port/linux/test/module-test.cpp @@ -940,19 +940,42 @@ static unsigned long mock_tick_ms(void) { return pika_platform_get_tick(); } -rpc_mapping gtest_rpc_map[] = {{"add", - [](cJSON* params[], int param_count) -> cJSON* { - int a = params[0]->valueint; - int b = params[1]->valueint; - return cJSON_CreateNumber(a + b); - }, - 2}, - {"get_val", - [](cJSON* params[], int param_count) -> cJSON* { - return cJSON_CreateNumber(2478); - }, - 0}, - RPC_MAP_END}; +rpc_mapping gtest_rpc_map[] = { + {"add", + [](cJSON* params[], int param_count) -> cJSON* { + int a = params[0]->valueint; + int b = params[1]->valueint; + return cJSON_CreateNumber(a + b); + }, + 2}, + {"get_val", + [](cJSON* params[], int param_count) -> cJSON* { + return cJSON_CreateNumber(2478); + }, + 0}, + {"concat", + [](cJSON* params[], int param_count) -> cJSON* { + const char* str1 = params[0]->valuestring; + const char* str2 = params[1]->valuestring; + size_t len = strlen(str1) + strlen(str2) + 1; + char* result_str = (char*)malloc(len); + if (!result_str) + return NULL; + strcpy(result_str, str1); + strcat(result_str, str2); + cJSON* result = cJSON_CreateString(result_str); + free(result_str); + return result; + }, + 2}, + {"multiply", + [](cJSON* params[], int param_count) -> cJSON* { + double a = params[0]->valuedouble; + double b = params[1]->valuedouble; + return cJSON_CreateNumber(a * b); + }, + 2}, + RPC_MAP_END}; rpc_mapping_nonblocking gtest_nonblocking_rpc_map[] = {RPC_MAP_END}; @@ -1313,4 +1336,39 @@ TEST(jrpc, exec_par_num_err) { EXPECT_STREQ(response, NULL); } +TEST(jrpc, exec_concat) { + char* response = execute_cmd("concat hello world"); + EXPECT_STREQ(response, "\"helloworld\""); + free(response); +} + +TEST(jrpc, exec_multiply) { + char* response = execute_cmd("multiply 3.5 2.0"); + EXPECT_STREQ(response, "7"); + free(response); +} + +TEST(jrpc, exec_multiply_integer) { + char* response = execute_cmd("multiply 3 2"); + EXPECT_STREQ(response, "6"); + free(response); +} + +TEST(jrpc, exec_concat_num_err) { + char* response = execute_cmd("concat hello"); + EXPECT_EQ(response, nullptr); +} + +TEST(jrpc, exec_concat_str) { + char* response = execute_cmd("concat \"hello\" \"world\""); + EXPECT_STREQ(response, "\"helloworld\""); + free(response); +} + +TEST(jrpc, exec_concat_str_space) { + char* response = execute_cmd("concat \"he llo\" \"world\""); + EXPECT_STREQ(response, "\"he lloworld\""); + free(response); +} + TEST_END diff --git a/src/PikaVersion.h b/src/PikaVersion.h index d96bac76a..033f88829 100644 --- a/src/PikaVersion.h +++ b/src/PikaVersion.h @@ -2,4 +2,4 @@ #define PIKA_VERSION_MINOR 13 #define PIKA_VERSION_MICRO 3 -#define PIKA_EDIT_TIME "2024/07/10 20:34:31" +#define PIKA_EDIT_TIME "2024/07/15 17:00:25"