소스 검색

Unescape

PUBLISHED_FROM=e42171cd840271166beb098c1d701b25ca0c3a53
Sergey Lyubka 9 년 전
부모
커밋
bd8c0c7f93
3개의 변경된 파일136개의 추가작업 그리고 83개의 파일을 삭제
  1. 59 38
      frozen.c
  2. 16 7
      frozen.h
  3. 61 38
      unit_test.c

+ 59 - 38
frozen.c

@@ -83,27 +83,24 @@ struct fstate {
   int path_len;
 };
 
-#define SET_STATE(fr, ptr, str, len)                \
-  struct fstate fstate = {(ptr), (fr)->path_len};   \
+#define SET_STATE(fr, ptr, str, len)              \
+  struct fstate fstate = {(ptr), (fr)->path_len}; \
   append_to_path((fr), (str), (len));
 
-#define CALL_BACK(fr, tok, value, len)                                    \
-  do {                                                                    \
-    if ((fr)->callback &&                                                 \
-        ((fr)->path_len == 0 || (fr)->path[(fr)->path_len - 1] != '.')){  \
-                                                                          \
-      struct json_token t = {(value), (len), (tok)};                      \
-                                                                          \
-      /* Call the callback with the given value and current name */       \
-      (fr)->callback(                                                     \
-          (fr)->callback_data,                                            \
-          (fr)->cur_name, (fr)->cur_name_len, (fr)->path, &t              \
-          );                                                              \
-                                                                          \
-      /* Reset the name */                                                \
-      (fr)->cur_name = NULL;                                              \
-      (fr)->cur_name_len = 0;                                             \
-    }                                                                     \
+#define CALL_BACK(fr, tok, value, len)                                        \
+  do {                                                                        \
+    if ((fr)->callback &&                                                     \
+        ((fr)->path_len == 0 || (fr)->path[(fr)->path_len - 1] != '.')) {     \
+      struct json_token t = {(value), (len), (tok)};                          \
+                                                                              \
+      /* Call the callback with the given value and current name */           \
+      (fr)->callback((fr)->callback_data, (fr)->cur_name, (fr)->cur_name_len, \
+                     (fr)->path, &t);                                         \
+                                                                              \
+      /* Reset the name */                                                    \
+      (fr)->cur_name = NULL;                                                  \
+      (fr)->cur_name_len = 0;                                                 \
+    }                                                                         \
   } while (0)
 
 static int append_to_path(struct frozen *f, const char *str, int size) {
@@ -288,8 +285,8 @@ static int parse_array(struct frozen *f) {
         snprintf(buf, sizeof(buf), "[%d]", i);
         i++;
         current_path_len = append_to_path(f, buf, strlen(buf));
-        f->cur_name = f->path + strlen(f->path) - strlen(buf) +
-          1 /*opening brace*/;
+        f->cur_name =
+            f->path + strlen(f->path) - strlen(buf) + 1 /*opening brace*/;
         f->cur_name_len = strlen(buf) - 2 /*braces*/;
         TRY(parse_value(f));
         truncate_path(f, current_path_len);
@@ -303,7 +300,8 @@ static int parse_array(struct frozen *f) {
   return 0;
 }
 
-static int expect(struct frozen *f, const char *s, int len, enum json_token_type tok_type) {
+static int expect(struct frozen *f, const char *s, int len,
+                  enum json_token_type tok_type) {
   int i, n = left(f);
   SET_STATE(f, f->cur, "", 0);
   for (i = 0; i < len; i++) {
@@ -540,7 +538,8 @@ int json_vprintf(struct json_out *out, const char *fmt, va_list xap) {
         fmt2[n + 1] = '\0';
 
         va_copy(sub_ap, ap);
-        need_len = vsnprintf(buf, sizeof(buf), fmt2, sub_ap) + 1 /* null-term */;
+        need_len =
+            vsnprintf(buf, sizeof(buf), fmt2, sub_ap) + 1 /* null-term */;
         /*
          * TODO(lsm): Fix windows & eCos code path here. Their vsnprintf
          * implementation returns -1 on overflow rather needed size.
@@ -550,7 +549,7 @@ int json_vprintf(struct json_out *out, const char *fmt, va_list xap) {
            * resulting string doesn't fit into a stack-allocated buffer `buf`,
            * so we need to allocate a new buffer from heap and use it
            */
-          pbuf = (char *)malloc(need_len);
+          pbuf = (char *) malloc(need_len);
           va_copy(sub_ap, ap);
           vsnprintf(pbuf, need_len, fmt2, sub_ap);
         }
@@ -687,9 +686,8 @@ struct scan_array_info {
   struct json_token *token;
 };
 
-static void json_scanf_array_elem_cb(void *callback_data,
-                                     const char *name, size_t name_len,
-                                     const char *path,
+static void json_scanf_array_elem_cb(void *callback_data, const char *name,
+                                     size_t name_len, const char *path,
                                      const struct json_token *token) {
   struct scan_array_info *info = (struct scan_array_info *) callback_data;
 
@@ -720,11 +718,34 @@ struct json_scanf_info {
   int type;
 };
 
-static void json_scanf_cb(void *callback_data,
-                          const char *name, size_t name_len,
-                          const char *path,
-                          const struct json_token *token)
-{
+int json_unescape(const char *src, int slen, char *dst, int dlen) {
+  char *send = (char *) src + slen, *dend = dst + dlen, *orig_dst = dst, *p;
+  const char *esc1 = "\"\\/bfnrt", *esc2 = "\"\\/\b\f\n\r\t";
+
+  while (src < send) {
+    if (*src == '\\') {
+      if (++src >= send) return JSON_STRING_INCOMPLETE;
+      if (*src == 'u') {
+        /* TODO(lsm): \uXXXX escapes drag utf8 lib... Do it at some stage */
+        return JSON_STRING_INVALID;
+      } else if ((p = (char *) strchr(esc1, *src)) != NULL) {
+        if (dst < dend) *dst = esc2[p - esc1];
+      } else {
+        return JSON_STRING_INVALID;
+      }
+    } else {
+      if (dst < dend) *dst = *src;
+    }
+    dst++;
+    src++;
+  }
+
+  return dst - orig_dst;
+}
+
+static void json_scanf_cb(void *callback_data, const char *name,
+                          size_t name_len, const char *path,
+                          const struct json_token *token) {
   struct json_scanf_info *info = (struct json_scanf_info *) callback_data;
 
   (void) name;
@@ -759,12 +780,12 @@ static void json_scanf_cb(void *callback_data,
     }
     case 'Q': {
       char **dst = (char **) info->target;
-      info->num_conversions++;
-      /* TODO(lsm): un-escape string */
-      *dst = (char *) malloc(token->len + 1);
-      if (*dst != NULL) {
-        strncpy(*dst, token->ptr, token->len);
-        (*dst)[token->len] = '\0';
+      int unescaped_len = json_unescape(token->ptr, token->len, NULL, 0);
+      if (unescaped_len >= 0 &&
+          (*dst = (char *) malloc(unescaped_len + 1)) != NULL) {
+        info->num_conversions++;
+        json_unescape(token->ptr, token->len, *dst, unescaped_len);
+        (*dst)[unescaped_len] = '\0';
       }
       break;
     }

+ 16 - 7
frozen.h

@@ -54,7 +54,8 @@ struct json_token {
   enum json_token_type type; /* Type of the token, possible values are above */
 };
 
-#define JSON_INVALID_TOKEN {0, 0, JSON_TYPE_INVALID}
+#define JSON_INVALID_TOKEN \
+  { 0, 0, JSON_TYPE_INVALID }
 
 /* Error codes */
 #define JSON_STRING_INVALID -1
@@ -79,13 +80,15 @@ struct json_token {
  * - type: JSON_TYPE_NUMBER, name: "1", path: ".bar[1]", value: "2"
  * - type: JSON_TYPE_OBJECT_START, name: "2", path: ".bar[2]", value: NULL
  * - type: JSON_TYPE_TRUE, name: "baz", path: ".bar[2].baz", value: "true"
- * - type: JSON_TYPE_OBJECT_END, name: NULL, path: ".bar[2]", value: "{ \"baz\": true }"
- * - type: JSON_TYPE_ARRAY_END, name: NULL, path: ".bar", value: "[ 1, 2, { \"baz\": true } ]"
- * - type: JSON_TYPE_OBJECT_END, name: NULL, path: "", value: "{ \"foo\": 123, \"bar\": [ 1, 2, { \"baz\": true } ] }"
+ * - type: JSON_TYPE_OBJECT_END, name: NULL, path: ".bar[2]", value: "{ \"baz\":
+ *true }"
+ * - type: JSON_TYPE_ARRAY_END, name: NULL, path: ".bar", value: "[ 1, 2, {
+ *\"baz\": true } ]"
+ * - type: JSON_TYPE_OBJECT_END, name: NULL, path: "", value: "{ \"foo\": 123,
+ *\"bar\": [ 1, 2, { \"baz\": true } ] }"
  */
-typedef void (*json_walk_callback_t)(void *callback_data,
-                                     const char *name, size_t name_len,
-                                     const char *path,
+typedef void (*json_walk_callback_t)(void *callback_data, const char *name,
+                                     size_t name_len, const char *path,
                                      const struct json_token *token);
 
 /*
@@ -183,6 +186,12 @@ typedef void (*json_scanner_t)(const char *str, int len, void *user_data);
 int json_scanf_array_elem(const char *s, int len, const char *path, int index,
                           struct json_token *token);
 
+/*
+ * Unescape JSON-encoded string src,slen into dst, dlen.
+ * src and dst may overlap.
+ */
+// size_t json_unescape(const char *src, size_t slen, char *dst, size_t dlen);
+
 #ifdef __cplusplus
 }
 #endif /* __cplusplus */

+ 61 - 38
unit_test.c

@@ -33,16 +33,8 @@
 #include <string.h>
 
 const char *tok_type_names[] = {
-  "INVALID",
-  "STRING",
-  "NUMBER",
-  "TRUE",
-  "FALSE",
-  "NULL",
-  "OBJECT_START",
-  "OBJECT_END",
-  "ARRAY_START",
-  "ARRAY_END",
+    "INVALID", "STRING",       "NUMBER",     "TRUE",        "FALSE",
+    "NULL",    "OBJECT_START", "OBJECT_END", "ARRAY_START", "ARRAY_END",
 };
 
 #define FAIL(str, line)                           \
@@ -276,8 +268,13 @@ static const char *test_json_printf(void) {
      * from heap)
      */
     struct json_out out = JSON_OUT_BUF(buf, sizeof(buf));
-    const char *result = "{\"foo\": \"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\"}";
-    json_printf(&out, "{foo: %s}", "\"1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\"");
+    const char *result =
+        "{\"foo\": "
+        "\"12345678901234567890123456789012345678901234567890123456789012345678"
+        "90123456789012345678901234567890\"}";
+    json_printf(&out, "{foo: %s}",
+                "\"123456789012345678901234567890123456789012345678901234567890"
+                "1234567890123456789012345678901234567890\"");
     ASSERT(strcmp(buf, result) == 0);
   }
 
@@ -328,44 +325,42 @@ static const char *test_system(void) {
   return NULL;
 }
 
-static void cb(void *data,
-               const char *name, size_t name_len,
-               const char *path,
+static void cb(void *data, const char *name, size_t name_len, const char *path,
                const struct json_token *token) {
   char *buf = (char *) data;
 
   const char *snull = "<null>";
 
   sprintf(buf + strlen(buf), "name:'%.*s', path:'%s', type:%s, val:'%.*s'\n",
-      (int) (name != NULL ? name_len : strlen(snull)),
-      name != NULL ? name : snull,
-      path,
-      tok_type_names[token->type],
-      (int) (token->ptr != NULL ? token->len : strlen(snull)),
-      token->ptr != NULL ? token->ptr : snull
-      );
+          (int) (name != NULL ? name_len : strlen(snull)),
+          name != NULL ? name : snull, path, tok_type_names[token->type],
+          (int) (token->ptr != NULL ? token->len : strlen(snull)),
+          token->ptr != NULL ? token->ptr : snull);
 }
 
 static const char *test_callback_api(void) {
   const char *s =
-    "{\"c\":[\"foo\", \"bar\", {\"a\":9, \"b\": \"x\"}], "
-    "\"mynull\": null, \"mytrue\": true, \"myfalse\": false}";
+      "{\"c\":[\"foo\", \"bar\", {\"a\":9, \"b\": \"x\"}], "
+      "\"mynull\": null, \"mytrue\": true, \"myfalse\": false}";
 
   const char *result =
-    "name:'<null>', path:'', type:OBJECT_START, val:'<null>'\n"
-    "name:'c', path:'.c', type:ARRAY_START, val:'<null>'\n"
-    "name:'0', path:'.c[0]', type:STRING, val:'foo'\n"
-    "name:'1', path:'.c[1]', type:STRING, val:'bar'\n"
-    "name:'2', path:'.c[2]', type:OBJECT_START, val:'<null>'\n"
-    "name:'a', path:'.c[2].a', type:NUMBER, val:'9'\n"
-    "name:'b', path:'.c[2].b', type:STRING, val:'x'\n"
-    "name:'<null>', path:'.c[2]', type:OBJECT_END, val:'{\"a\":9, \"b\": \"x\"}'\n"
-    "name:'<null>', path:'.c', type:ARRAY_END, val:'[\"foo\", \"bar\", {\"a\":9, \"b\": \"x\"}]'\n"
-    "name:'mynull', path:'.mynull', type:NULL, val:'null'\n"
-    "name:'mytrue', path:'.mytrue', type:TRUE, val:'true'\n"
-    "name:'myfalse', path:'.myfalse', type:FALSE, val:'false'\n"
-    "name:'<null>', path:'', type:OBJECT_END, val:'{\"c\":[\"foo\", \"bar\", {\"a\":9, \"b\": \"x\"}], \"mynull\": null, \"mytrue\": true, \"myfalse\": false}'\n"
-    ;
+      "name:'<null>', path:'', type:OBJECT_START, val:'<null>'\n"
+      "name:'c', path:'.c', type:ARRAY_START, val:'<null>'\n"
+      "name:'0', path:'.c[0]', type:STRING, val:'foo'\n"
+      "name:'1', path:'.c[1]', type:STRING, val:'bar'\n"
+      "name:'2', path:'.c[2]', type:OBJECT_START, val:'<null>'\n"
+      "name:'a', path:'.c[2].a', type:NUMBER, val:'9'\n"
+      "name:'b', path:'.c[2].b', type:STRING, val:'x'\n"
+      "name:'<null>', path:'.c[2]', type:OBJECT_END, val:'{\"a\":9, \"b\": "
+      "\"x\"}'\n"
+      "name:'<null>', path:'.c', type:ARRAY_END, val:'[\"foo\", \"bar\", "
+      "{\"a\":9, \"b\": \"x\"}]'\n"
+      "name:'mynull', path:'.mynull', type:NULL, val:'null'\n"
+      "name:'mytrue', path:'.mytrue', type:TRUE, val:'true'\n"
+      "name:'myfalse', path:'.myfalse', type:FALSE, val:'false'\n"
+      "name:'<null>', path:'', type:OBJECT_END, val:'{\"c\":[\"foo\", \"bar\", "
+      "{\"a\":9, \"b\": \"x\"}], \"mynull\": null, \"mytrue\": true, "
+      "\"myfalse\": false}'\n";
 
   char buf[4096] = "";
   ASSERT(json_walk(s, strlen(s), cb, buf) == (int) strlen(s));
@@ -434,6 +429,7 @@ static const char *test_scanf(void) {
     ASSERT(json_scanf(str, strlen(str), "{a: %Q}", &s) == 1);
     ASSERT(s != NULL);
     ASSERT(s[3] == '\0');
+    free(s);
   }
 
   {
@@ -461,6 +457,32 @@ static const char *test_scanf(void) {
     ASSERT(i == 2);
   }
 
+  {
+    const char *str = "{a : \"foo\\b\\f\\n\\r\\t\\\\\" }";
+    char *result;
+    ASSERT(json_scanf(str, strlen(str), "{a: %Q}", &result) == 1);
+    ASSERT(strcmp(result, "foo\b\f\n\r\t\\") == 0);
+    free(result);
+
+    ASSERT(json_scanf(str, 9, "{a: %Q}", &result) == 0);
+  }
+
+  {
+    const char *str = "{a : \"привет\" }";
+    char *result;
+    ASSERT(json_scanf(str, strlen(str), "{a: %Q}", &result) == 1);
+    ASSERT(strcmp(result, "привет") == 0);
+    free(result);
+  }
+
+  return NULL;
+}
+
+static const char *test_json_unescape(void) {
+  ASSERT(json_unescape("foo", 3, NULL, 0) == 3);
+  ASSERT(json_unescape("foo\\", 4, NULL, 0) == JSON_STRING_INCOMPLETE);
+  ASSERT(json_unescape("foo\\x", 5, NULL, 0) == JSON_STRING_INVALID);
+  ASSERT(json_unescape("\\ueeee", 5, NULL, 0) == JSON_STRING_INVALID);
   return NULL;
 }
 
@@ -470,6 +492,7 @@ static const char *run_all_tests(void) {
   RUN_TEST(test_json_printf);
   RUN_TEST(test_system);
   RUN_TEST(test_callback_api);
+  RUN_TEST(test_json_unescape);
   return NULL;
 }