From 60500ff42774f77b2832ad0bc5b4de8323d9133d Mon Sep 17 00:00:00 2001
From: Lyon <liang6516@outlook.com>
Date: Mon, 15 Jul 2024 17:00:36 +0800
Subject: [PATCH] jrpc_cmd support parse float and string

---
 package/jrpc/jrpc.c                           | 101 +++++++++++++++---
 package/jrpc/jrpc.h                           |   2 +-
 port/linux/.vscode/launch.json                |   3 +-
 .../pikascript/pikascript-lib/jrpc/jrpc.c     | 101 +++++++++++++++---
 .../pikascript/pikascript-lib/jrpc/jrpc.h     |   2 +-
 port/linux/test/module-test.cpp               |  84 ++++++++++++---
 src/PikaVersion.h                             |   2 +-
 7 files changed, 250 insertions(+), 45 deletions(-)

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"