diff options
| author | Omar Rizwan <omar@omar.website> | 2020-12-27 18:20:34 -0800 | 
|---|---|---|
| committer | Omar Rizwan <omar@omar.website> | 2020-12-27 18:20:34 -0800 | 
| commit | e31f915bddaaf0484a558d45dc95dd44dc920dad (patch) | |
| tree | a3d9bc7a100e14634ca8379f3d9c65f9ca8b2de2 /fs | |
| parent | fb6ed2b2f472c814f2f24d12e9b24a19f850604b (diff) | |
vendor frozen
Diffstat (limited to 'fs')
| m--------- | fs/frozen | 0 | ||||
| -rw-r--r-- | fs/tabfs.c | 4 | ||||
| -rw-r--r-- | fs/vendor/frozen.c | 1471 | ||||
| -rw-r--r-- | fs/vendor/frozen.h | 329 | 
4 files changed, 1802 insertions, 2 deletions
diff --git a/fs/frozen b/fs/frozen deleted file mode 160000 -Subproject 365a7dddd0beba7dbef43dc405a6c5ffa5bddf0 @@ -11,8 +11,8 @@  #include <pthread.h>  #include <fuse.h> -#include "frozen/frozen.h" -#include "frozen/frozen.c" +#include "vendor/frozen.h" +#include "vendor/frozen.c"  FILE* l; diff --git a/fs/vendor/frozen.c b/fs/vendor/frozen.c new file mode 100644 index 0000000..4a6af16 --- /dev/null +++ b/fs/vendor/frozen.c @@ -0,0 +1,1471 @@ +/* + * Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com> + * Copyright (c) 2018 Cesanta Software Limited + * All rights reserved + * + * Licensed under the Apache License, Version 2.0 (the ""License""); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *     http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an ""AS IS"" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define _CRT_SECURE_NO_WARNINGS /* Disable deprecation warning in VS2005+ */ + +#include "frozen.h" + +#include <ctype.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#if !defined(WEAK) +#if (defined(__GNUC__) || defined(__TI_COMPILER_VERSION__)) && !defined(_WIN32) +#define WEAK __attribute__((weak)) +#else +#define WEAK +#endif +#endif + +#ifdef _WIN32 +#undef snprintf +#undef vsnprintf +#define snprintf cs_win_snprintf +#define vsnprintf cs_win_vsnprintf +int cs_win_snprintf(char *str, size_t size, const char *format, ...); +int cs_win_vsnprintf(char *str, size_t size, const char *format, va_list ap); +#if _MSC_VER >= 1700 || (defined(__GNUC__)) +#include <stdint.h> +#else +typedef _int64 int64_t; +typedef unsigned _int64 uint64_t; +#endif +#define PRId64 "I64d" +#define PRIu64 "I64u" +#else /* _WIN32 */ +/* <inttypes.h> wants this for C++ */ +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif +#include <inttypes.h> +#endif /* _WIN32 */ + +#ifndef INT64_FMT +#define INT64_FMT PRId64 +#endif +#ifndef UINT64_FMT +#define UINT64_FMT PRIu64 +#endif + +#ifndef va_copy +#define va_copy(x, y) x = y +#endif + +#ifndef JSON_ENABLE_ARRAY +#define JSON_ENABLE_ARRAY 1 +#endif + +struct frozen { +  const char *end; +  const char *cur; + +  const char *cur_name; +  size_t cur_name_len; + +  /* For callback API */ +  char path[JSON_MAX_PATH_LEN]; +  size_t path_len; +  void *callback_data; +  json_walk_callback_t callback; +}; + +struct fstate { +  const char *ptr; +  size_t path_len; +}; + +#define SET_STATE(fr, ptr, str, len)              \ +  struct fstate fstate = {(ptr), (fr)->path_len}; \ +  json_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), (int) (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 json_append_to_path(struct frozen *f, const char *str, int size) { +  int n = f->path_len; +  int left = sizeof(f->path) - n - 1; +  if (size > left) size = left; +  memcpy(f->path + n, str, size); +  f->path[n + size] = '\0'; +  f->path_len += size; +  return n; +} + +static void json_truncate_path(struct frozen *f, size_t len) { +  f->path_len = len; +  f->path[len] = '\0'; +} + +static int json_parse_object(struct frozen *f); +static int json_parse_value(struct frozen *f); + +#define EXPECT(cond, err_code)      \ +  do {                              \ +    if (!(cond)) return (err_code); \ +  } while (0) + +#define TRY(expr)          \ +  do {                     \ +    int _n = expr;         \ +    if (_n < 0) return _n; \ +  } while (0) + +#define END_OF_STRING (-1) + +static int json_left(const struct frozen *f) { +  return f->end - f->cur; +} + +static int json_isspace(int ch) { +  return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n'; +} + +static void json_skip_whitespaces(struct frozen *f) { +  while (f->cur < f->end && json_isspace(*f->cur)) f->cur++; +} + +static int json_cur(struct frozen *f) { +  json_skip_whitespaces(f); +  return f->cur >= f->end ? END_OF_STRING : *(unsigned char *) f->cur; +} + +static int json_test_and_skip(struct frozen *f, int expected) { +  int ch = json_cur(f); +  if (ch == expected) { +    f->cur++; +    return 0; +  } +  return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID; +} + +static int json_isalpha(int ch) { +  return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'); +} + +static int json_isdigit(int ch) { +  return ch >= '0' && ch <= '9'; +} + +static int json_isxdigit(int ch) { +  return json_isdigit(ch) || (ch >= 'a' && ch <= 'f') || +         (ch >= 'A' && ch <= 'F'); +} + +static int json_get_escape_len(const char *s, int len) { +  switch (*s) { +    case 'u': +      return len < 6 ? JSON_STRING_INCOMPLETE +                     : json_isxdigit(s[1]) && json_isxdigit(s[2]) && +                               json_isxdigit(s[3]) && json_isxdigit(s[4]) +                           ? 5 +                           : JSON_STRING_INVALID; +    case '"': +    case '\\': +    case '/': +    case 'b': +    case 'f': +    case 'n': +    case 'r': +    case 't': +      return len < 2 ? JSON_STRING_INCOMPLETE : 1; +    default: +      return JSON_STRING_INVALID; +  } +} + +/* identifier = letter { letter | digit | '_' } */ +static int json_parse_identifier(struct frozen *f) { +  EXPECT(json_isalpha(json_cur(f)), JSON_STRING_INVALID); +  { +    SET_STATE(f, f->cur, "", 0); +    while (f->cur < f->end && +           (*f->cur == '_' || json_isalpha(*f->cur) || json_isdigit(*f->cur))) { +      f->cur++; +    } +    json_truncate_path(f, fstate.path_len); +    CALL_BACK(f, JSON_TYPE_STRING, fstate.ptr, f->cur - fstate.ptr); +  } +  return 0; +} + +static int json_get_utf8_char_len(unsigned char ch) { +  if ((ch & 0x80) == 0) return 1; +  switch (ch & 0xf0) { +    case 0xf0: +      return 4; +    case 0xe0: +      return 3; +    default: +      return 2; +  } +} + +/* string = '"' { quoted_printable_chars } '"' */ +static int json_parse_string(struct frozen *f) { +  int n, ch = 0, len = 0; +  TRY(json_test_and_skip(f, '"')); +  { +    SET_STATE(f, f->cur, "", 0); +    for (; f->cur < f->end; f->cur += len) { +      ch = *(unsigned char *) f->cur; +      len = json_get_utf8_char_len((unsigned char) ch); +      EXPECT(ch >= 32 && len > 0, JSON_STRING_INVALID); /* No control chars */ +      EXPECT(len <= json_left(f), JSON_STRING_INCOMPLETE); +      if (ch == '\\') { +        EXPECT((n = json_get_escape_len(f->cur + 1, json_left(f))) > 0, n); +        len += n; +      } else if (ch == '"') { +        json_truncate_path(f, fstate.path_len); +        CALL_BACK(f, JSON_TYPE_STRING, fstate.ptr, f->cur - fstate.ptr); +        f->cur++; +        break; +      }; +    } +  } +  return ch == '"' ? 0 : JSON_STRING_INCOMPLETE; +} + +/* number = [ '-' ] digit+ [ '.' digit+ ] [ ['e'|'E'] ['+'|'-'] digit+ ] */ +static int json_parse_number(struct frozen *f) { +  int ch = json_cur(f); +  SET_STATE(f, f->cur, "", 0); +  if (ch == '-') f->cur++; +  EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE); +  if (f->cur + 1 < f->end && f->cur[0] == '0' && f->cur[1] == 'x') { +    f->cur += 2; +    EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE); +    EXPECT(json_isxdigit(f->cur[0]), JSON_STRING_INVALID); +    while (f->cur < f->end && json_isxdigit(f->cur[0])) f->cur++; +  } else { +    EXPECT(json_isdigit(f->cur[0]), JSON_STRING_INVALID); +    while (f->cur < f->end && json_isdigit(f->cur[0])) f->cur++; +    if (f->cur < f->end && f->cur[0] == '.') { +      f->cur++; +      EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE); +      EXPECT(json_isdigit(f->cur[0]), JSON_STRING_INVALID); +      while (f->cur < f->end && json_isdigit(f->cur[0])) f->cur++; +    } +    if (f->cur < f->end && (f->cur[0] == 'e' || f->cur[0] == 'E')) { +      f->cur++; +      EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE); +      if ((f->cur[0] == '+' || f->cur[0] == '-')) f->cur++; +      EXPECT(f->cur < f->end, JSON_STRING_INCOMPLETE); +      EXPECT(json_isdigit(f->cur[0]), JSON_STRING_INVALID); +      while (f->cur < f->end && json_isdigit(f->cur[0])) f->cur++; +    } +  } +  json_truncate_path(f, fstate.path_len); +  CALL_BACK(f, JSON_TYPE_NUMBER, fstate.ptr, f->cur - fstate.ptr); +  return 0; +} + +#if JSON_ENABLE_ARRAY +/* array = '[' [ value { ',' value } ] ']' */ +static int json_parse_array(struct frozen *f) { +  int i = 0, current_path_len; +  char buf[20]; +  CALL_BACK(f, JSON_TYPE_ARRAY_START, NULL, 0); +  TRY(json_test_and_skip(f, '[')); +  { +    { +      SET_STATE(f, f->cur - 1, "", 0); +      while (json_cur(f) != ']') { +        snprintf(buf, sizeof(buf), "[%d]", i); +        i++; +        current_path_len = json_append_to_path(f, buf, strlen(buf)); +        f->cur_name = +            f->path + strlen(f->path) - strlen(buf) + 1 /*opening brace*/; +        f->cur_name_len = strlen(buf) - 2 /*braces*/; +        TRY(json_parse_value(f)); +        json_truncate_path(f, current_path_len); +        if (json_cur(f) == ',') f->cur++; +      } +      TRY(json_test_and_skip(f, ']')); +      json_truncate_path(f, fstate.path_len); +      CALL_BACK(f, JSON_TYPE_ARRAY_END, fstate.ptr, f->cur - fstate.ptr); +    } +  } +  return 0; +} +#endif /* JSON_ENABLE_ARRAY */ + +static int json_expect(struct frozen *f, const char *s, int len, +                       enum json_token_type tok_type) { +  int i, n = json_left(f); +  SET_STATE(f, f->cur, "", 0); +  for (i = 0; i < len; i++) { +    if (i >= n) return JSON_STRING_INCOMPLETE; +    if (f->cur[i] != s[i]) return JSON_STRING_INVALID; +  } +  f->cur += len; +  json_truncate_path(f, fstate.path_len); + +  CALL_BACK(f, tok_type, fstate.ptr, f->cur - fstate.ptr); + +  return 0; +} + +/* value = 'null' | 'true' | 'false' | number | string | array | object */ +static int json_parse_value(struct frozen *f) { +  int ch = json_cur(f); + +  switch (ch) { +    case '"': +      TRY(json_parse_string(f)); +      break; +    case '{': +      TRY(json_parse_object(f)); +      break; +#if JSON_ENABLE_ARRAY +    case '[': +      TRY(json_parse_array(f)); +      break; +#endif +    case 'n': +      TRY(json_expect(f, "null", 4, JSON_TYPE_NULL)); +      break; +    case 't': +      TRY(json_expect(f, "true", 4, JSON_TYPE_TRUE)); +      break; +    case 'f': +      TRY(json_expect(f, "false", 5, JSON_TYPE_FALSE)); +      break; +    case '-': +    case '0': +    case '1': +    case '2': +    case '3': +    case '4': +    case '5': +    case '6': +    case '7': +    case '8': +    case '9': +      TRY(json_parse_number(f)); +      break; +    default: +      return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID; +  } + +  return 0; +} + +/* key = identifier | string */ +static int json_parse_key(struct frozen *f) { +  int ch = json_cur(f); +  if (json_isalpha(ch)) { +    TRY(json_parse_identifier(f)); +  } else if (ch == '"') { +    TRY(json_parse_string(f)); +  } else { +    return ch == END_OF_STRING ? JSON_STRING_INCOMPLETE : JSON_STRING_INVALID; +  } +  return 0; +} + +/* pair = key ':' value */ +static int json_parse_pair(struct frozen *f) { +  int current_path_len; +  const char *tok; +  json_skip_whitespaces(f); +  tok = f->cur; +  TRY(json_parse_key(f)); +  { +    f->cur_name = *tok == '"' ? tok + 1 : tok; +    f->cur_name_len = *tok == '"' ? f->cur - tok - 2 : f->cur - tok; +    current_path_len = json_append_to_path(f, f->cur_name, f->cur_name_len); +  } +  TRY(json_test_and_skip(f, ':')); +  TRY(json_parse_value(f)); +  json_truncate_path(f, current_path_len); +  return 0; +} + +/* object = '{' pair { ',' pair } '}' */ +static int json_parse_object(struct frozen *f) { +  CALL_BACK(f, JSON_TYPE_OBJECT_START, NULL, 0); +  TRY(json_test_and_skip(f, '{')); +  { +    SET_STATE(f, f->cur - 1, ".", 1); +    while (json_cur(f) != '}') { +      TRY(json_parse_pair(f)); +      if (json_cur(f) == ',') f->cur++; +    } +    TRY(json_test_and_skip(f, '}')); +    json_truncate_path(f, fstate.path_len); +    CALL_BACK(f, JSON_TYPE_OBJECT_END, fstate.ptr, f->cur - fstate.ptr); +  } +  return 0; +} + +static int json_doit(struct frozen *f) { +  if (f->cur == 0 || f->end < f->cur) return JSON_STRING_INVALID; +  if (f->end == f->cur) return JSON_STRING_INCOMPLETE; +  return json_parse_value(f); +} + +int json_escape(struct json_out *out, const char *p, size_t len) WEAK; +int json_escape(struct json_out *out, const char *p, size_t len) { +  size_t i, cl, n = 0; +  const char *hex_digits = "0123456789abcdef"; +  const char *specials = "btnvfr"; + +  for (i = 0; i < len; i++) { +    unsigned char ch = ((unsigned char *) p)[i]; +    if (ch == '"' || ch == '\\') { +      n += out->printer(out, "\\", 1); +      n += out->printer(out, p + i, 1); +    } else if (ch >= '\b' && ch <= '\r') { +      n += out->printer(out, "\\", 1); +      n += out->printer(out, &specials[ch - '\b'], 1); +    } else if (isprint(ch)) { +      n += out->printer(out, p + i, 1); +    } else if ((cl = json_get_utf8_char_len(ch)) == 1) { +      n += out->printer(out, "\\u00", 4); +      n += out->printer(out, &hex_digits[(ch >> 4) % 0xf], 1); +      n += out->printer(out, &hex_digits[ch % 0xf], 1); +    } else { +      n += out->printer(out, p + i, cl); +      i += cl - 1; +    } +  } + +  return n; +} + +int json_printer_buf(struct json_out *out, const char *buf, size_t len) WEAK; +int json_printer_buf(struct json_out *out, const char *buf, size_t len) { +  size_t avail = out->u.buf.size - out->u.buf.len; +  size_t n = len < avail ? len : avail; +  memcpy(out->u.buf.buf + out->u.buf.len, buf, n); +  out->u.buf.len += n; +  if (out->u.buf.size > 0) { +    size_t idx = out->u.buf.len; +    if (idx >= out->u.buf.size) idx = out->u.buf.size - 1; +    out->u.buf.buf[idx] = '\0'; +  } +  return len; +} + +int json_printer_file(struct json_out *out, const char *buf, size_t len) WEAK; +int json_printer_file(struct json_out *out, const char *buf, size_t len) { +  return fwrite(buf, 1, len, out->u.fp); +} + +#if JSON_ENABLE_BASE64 +static int b64idx(int c) { +  if (c < 26) { +    return c + 'A'; +  } else if (c < 52) { +    return c - 26 + 'a'; +  } else if (c < 62) { +    return c - 52 + '0'; +  } else { +    return c == 62 ? '+' : '/'; +  } +} + +static int b64rev(int c) { +  if (c >= 'A' && c <= 'Z') { +    return c - 'A'; +  } else if (c >= 'a' && c <= 'z') { +    return c + 26 - 'a'; +  } else if (c >= '0' && c <= '9') { +    return c + 52 - '0'; +  } else if (c == '+') { +    return 62; +  } else if (c == '/') { +    return 63; +  } else { +    return 64; +  } +} + +static int b64enc(struct json_out *out, const unsigned char *p, int n) { +  char buf[4]; +  int i, len = 0; +  for (i = 0; i < n; i += 3) { +    int a = p[i], b = i + 1 < n ? p[i + 1] : 0, c = i + 2 < n ? p[i + 2] : 0; +    buf[0] = b64idx(a >> 2); +    buf[1] = b64idx((a & 3) << 4 | (b >> 4)); +    buf[2] = b64idx((b & 15) << 2 | (c >> 6)); +    buf[3] = b64idx(c & 63); +    if (i + 1 >= n) buf[2] = '='; +    if (i + 2 >= n) buf[3] = '='; +    len += out->printer(out, buf, sizeof(buf)); +  } +  return len; +} + +static int b64dec(const char *src, int n, char *dst) { +  const char *end = src + n; +  int len = 0; +  while (src + 3 < end) { +    int a = b64rev(src[0]), b = b64rev(src[1]), c = b64rev(src[2]), +        d = b64rev(src[3]); +    dst[len++] = (a << 2) | (b >> 4); +    if (src[2] != '=') { +      dst[len++] = (b << 4) | (c >> 2); +      if (src[3] != '=') { +        dst[len++] = (c << 6) | d; +      } +    } +    src += 4; +  } +  return len; +} +#endif /* JSON_ENABLE_BASE64 */ + +static unsigned char hexdec(const char *s) { +#define HEXTOI(x) (x >= '0' && x <= '9' ? x - '0' : x - 'W') +  int a = tolower(*(const unsigned char *) s); +  int b = tolower(*(const unsigned char *) (s + 1)); +  return (HEXTOI(a) << 4) | HEXTOI(b); +} + +int json_vprintf(struct json_out *out, const char *fmt, va_list xap) WEAK; +int json_vprintf(struct json_out *out, const char *fmt, va_list xap) { +  int len = 0; +  const char *quote = "\"", *null = "null"; +  va_list ap; +  va_copy(ap, xap); + +  while (*fmt != '\0') { +    if (strchr(":, \r\n\t[]{}\"", *fmt) != NULL) { +      len += out->printer(out, fmt, 1); +      fmt++; +    } else if (fmt[0] == '%') { +      char buf[21]; +      size_t skip = 2; + +      if (fmt[1] == 'l' && fmt[2] == 'l' && (fmt[3] == 'd' || fmt[3] == 'u')) { +        int64_t val = va_arg(ap, int64_t); +        const char *fmt2 = fmt[3] == 'u' ? "%" UINT64_FMT : "%" INT64_FMT; +        snprintf(buf, sizeof(buf), fmt2, val); +        len += out->printer(out, buf, strlen(buf)); +        skip += 2; +      } else if (fmt[1] == 'z' && fmt[2] == 'u') { +        size_t val = va_arg(ap, size_t); +        snprintf(buf, sizeof(buf), "%lu", (unsigned long) val); +        len += out->printer(out, buf, strlen(buf)); +        skip += 1; +      } else if (fmt[1] == 'M') { +        json_printf_callback_t f = va_arg(ap, json_printf_callback_t); +        len += f(out, &ap); +      } else if (fmt[1] == 'B') { +        int val = va_arg(ap, int); +        const char *str = val ? "true" : "false"; +        len += out->printer(out, str, strlen(str)); +      } else if (fmt[1] == 'H') { +#if JSON_ENABLE_HEX +        const char *hex = "0123456789abcdef"; +        int i, n = va_arg(ap, int); +        const unsigned char *p = va_arg(ap, const unsigned char *); +        len += out->printer(out, quote, 1); +        for (i = 0; i < n; i++) { +          len += out->printer(out, &hex[(p[i] >> 4) & 0xf], 1); +          len += out->printer(out, &hex[p[i] & 0xf], 1); +        } +        len += out->printer(out, quote, 1); +#endif /* JSON_ENABLE_HEX */ +      } else if (fmt[1] == 'V') { +#if JSON_ENABLE_BASE64 +        const unsigned char *p = va_arg(ap, const unsigned char *); +        int n = va_arg(ap, int); +        len += out->printer(out, quote, 1); +        len += b64enc(out, p, n); +        len += out->printer(out, quote, 1); +#endif /* JSON_ENABLE_BASE64 */ +      } else if (fmt[1] == 'Q' || +                 (fmt[1] == '.' && fmt[2] == '*' && fmt[3] == 'Q')) { +        size_t l = 0; +        const char *p; + +        if (fmt[1] == '.') { +          l = (size_t) va_arg(ap, int); +          skip += 2; +        } +        p = va_arg(ap, char *); + +        if (p == NULL) { +          len += out->printer(out, null, 4); +        } else { +          if (fmt[1] == 'Q') { +            l = strlen(p); +          } +          len += out->printer(out, quote, 1); +          len += json_escape(out, p, l); +          len += out->printer(out, quote, 1); +        } +      } else { +        /* +         * we delegate printing to the system printf. +         * The goal here is to delegate all modifiers parsing to the system +         * printf, as you can see below we still have to parse the format +         * types. +         * +         * Currently, %s with strings longer than 20 chars will require +         * double-buffering (an auxiliary buffer will be allocated from heap). +         * TODO(dfrank): reimplement %s and %.*s in order to avoid that. +         */ + +        const char *end_of_format_specifier = "sdfFeEgGlhuIcx.*-0123456789"; +        int n = strspn(fmt + 1, end_of_format_specifier); +        char *pbuf = buf; +        int need_len, size = sizeof(buf); +        char fmt2[20]; +        va_list ap_copy; +        strncpy(fmt2, fmt, +                n + 1 > (int) sizeof(fmt2) ? sizeof(fmt2) : (size_t) n + 1); +        fmt2[n + 1] = '\0'; + +        va_copy(ap_copy, ap); +        need_len = vsnprintf(pbuf, size, fmt2, ap_copy); +        va_end(ap_copy); + +        if (need_len < 0) { +          /* +           * Windows & eCos vsnprintf implementation return -1 on overflow +           * instead of needed size. +           */ +          pbuf = NULL; +          while (need_len < 0) { +            free(pbuf); +            size *= 2; +            if ((pbuf = (char *) malloc(size)) == NULL) break; +            va_copy(ap_copy, ap); +            need_len = vsnprintf(pbuf, size, fmt2, ap_copy); +            va_end(ap_copy); +          } +        } else if (need_len >= (int) sizeof(buf)) { +          /* +           * resulting string doesn't fit into a stack-allocated buffer `buf`, +           * so we need to allocate a new buffer from heap and use it +           */ +          if ((pbuf = (char *) malloc(need_len + 1)) != NULL) { +            va_copy(ap_copy, ap); +            vsnprintf(pbuf, need_len + 1, fmt2, ap_copy); +            va_end(ap_copy); +          } +        } +        if (pbuf == NULL) { +          buf[0] = '\0'; +          pbuf = buf; +        } + +        /* +         * however we need to parse the type ourselves in order to advance +         * the va_list by the correct amount; there is no portable way to +         * inherit the advancement made by vprintf. +         * 32-bit (linux or windows) passes va_list by value. +         */ +        if ((n + 1 == (int) strlen("%" PRId64) && +             strcmp(fmt2, "%" PRId64) == 0) || +            (n + 1 == (int) strlen("%" PRIu64) && +             strcmp(fmt2, "%" PRIu64) == 0)) { +          (void) va_arg(ap, int64_t); +        } else if (strcmp(fmt2, "%.*s") == 0) { +          (void) va_arg(ap, int); +          (void) va_arg(ap, char *); +        } else { +          switch (fmt2[n]) { +            case 'u': +            case 'd': +              (void) va_arg(ap, int); +              break; +            case 'g': +            case 'f': +              (void) va_arg(ap, double); +              break; +            case 'p': +              (void) va_arg(ap, void *); +              break; +            default: +              /* many types are promoted to int */ +              (void) va_arg(ap, int); +          } +        } + +        len += out->printer(out, pbuf, strlen(pbuf)); +        skip = n + 1; + +        /* If buffer was allocated from heap, free it */ +        if (pbuf != buf) { +          free(pbuf); +          pbuf = NULL; +        } +      } +      fmt += skip; +    } else if (*fmt == '_' || json_isalpha(*fmt)) { +      len += out->printer(out, quote, 1); +      while (*fmt == '_' || json_isalpha(*fmt) || json_isdigit(*fmt)) { +        len += out->printer(out, fmt, 1); +        fmt++; +      } +      len += out->printer(out, quote, 1); +    } else { +      len += out->printer(out, fmt, 1); +      fmt++; +    } +  } +  va_end(ap); + +  return len; +} + +int json_printf(struct json_out *out, const char *fmt, ...) WEAK; +int json_printf(struct json_out *out, const char *fmt, ...) { +  int n; +  va_list ap; +  va_start(ap, fmt); +  n = json_vprintf(out, fmt, ap); +  va_end(ap); +  return n; +} + +int json_printf_array(struct json_out *out, va_list *ap) WEAK; +int json_printf_array(struct json_out *out, va_list *ap) { +  int len = 0; +  char *arr = va_arg(*ap, char *); +  size_t i, arr_size = va_arg(*ap, size_t); +  size_t elem_size = va_arg(*ap, size_t); +  const char *fmt = va_arg(*ap, char *); +  len += json_printf(out, "[", 1); +  for (i = 0; arr != NULL && i < arr_size / elem_size; i++) { +    union { +      int64_t i; +      double d; +    } val; +    memcpy(&val, arr + i * elem_size, +           elem_size > sizeof(val) ? sizeof(val) : elem_size); +    if (i > 0) len += json_printf(out, ", "); +    if (strpbrk(fmt, "efg") != NULL) { +      len += json_printf(out, fmt, val.d); +    } else { +      len += json_printf(out, fmt, val.i); +    } +  } +  len += json_printf(out, "]", 1); +  return len; +} + +#ifdef _WIN32 +int cs_win_vsnprintf(char *str, size_t size, const char *format, +                     va_list ap) WEAK; +int cs_win_vsnprintf(char *str, size_t size, const char *format, va_list ap) { +  int res = _vsnprintf(str, size, format, ap); +  va_end(ap); +  if (res >= size) { +    str[size - 1] = '\0'; +  } +  return res; +} + +int cs_win_snprintf(char *str, size_t size, const char *format, ...) WEAK; +int cs_win_snprintf(char *str, size_t size, const char *format, ...) { +  int res; +  va_list ap; +  va_start(ap, format); +  res = vsnprintf(str, size, format, ap); +  va_end(ap); +  return res; +} +#endif /* _WIN32 */ + +int json_walk(const char *json_string, int json_string_length, +              json_walk_callback_t callback, void *callback_data) WEAK; +int json_walk(const char *json_string, int json_string_length, +              json_walk_callback_t callback, void *callback_data) { +  struct frozen frozen; + +  memset(&frozen, 0, sizeof(frozen)); +  frozen.end = json_string + json_string_length; +  frozen.cur = json_string; +  frozen.callback_data = callback_data; +  frozen.callback = callback; + +  TRY(json_doit(&frozen)); + +  return frozen.cur - json_string; +} + +struct scan_array_info { +  int found; +  char path[JSON_MAX_PATH_LEN]; +  struct json_token *token; +}; + +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; + +  (void) name; +  (void) name_len; + +  if (strcmp(path, info->path) == 0) { +    *info->token = *token; +    info->found = 1; +  } +} + +int json_scanf_array_elem(const char *s, int len, const char *path, int idx, +                          struct json_token *token) WEAK; +int json_scanf_array_elem(const char *s, int len, const char *path, int idx, +                          struct json_token *token) { +  struct scan_array_info info; +  info.token = token; +  info.found = 0; +  memset(token, 0, sizeof(*token)); +  snprintf(info.path, sizeof(info.path), "%s[%d]", path, idx); +  json_walk(s, len, json_scanf_array_elem_cb, &info); +  return info.found ? token->len : -1; +} + +struct json_scanf_info { +  int num_conversions; +  char *path; +  const char *fmt; +  void *target; +  void *user_data; +  int type; +}; + +int json_unescape(const char *src, int slen, char *dst, int dlen) WEAK; +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') { +        if (send - src < 5) return JSON_STRING_INCOMPLETE; +        /* Here we go: this is a \u.... escape. Process simple one-byte chars */ +        if (src[1] == '0' && src[2] == '0') { +          /* This is \u00xx character from the ASCII range */ +          if (dst < dend) *dst = hexdec(src + 3); +          src += 4; +        } else { +          /* Complex \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; +  char buf[32]; /* Must be enough to hold numbers */ + +  (void) name; +  (void) name_len; + +  if (token->ptr == NULL) { +    /* +     * We're not interested here in the events for which we have no value; +     * namely, JSON_TYPE_OBJECT_START and JSON_TYPE_ARRAY_START +     */ +    return; +  } + +  if (strcmp(path, info->path) != 0) { +    /* It's not the path we're looking for, so, just ignore this callback */ +    return; +  } + +  switch (info->type) { +    case 'B': +      info->num_conversions++; +      switch (sizeof(bool)) { +        case sizeof(char): +          *(char *) info->target = (token->type == JSON_TYPE_TRUE ? 1 : 0); +          break; +        case sizeof(int): +          *(int *) info->target = (token->type == JSON_TYPE_TRUE ? 1 : 0); +          break; +        default: +          /* should never be here */ +          abort(); +      } +      break; +    case 'M': { +      union { +        void *p; +        json_scanner_t f; +      } u = {info->target}; +      info->num_conversions++; +      u.f(token->ptr, token->len, info->user_data); +      break; +    } +    case 'Q': { +      char **dst = (char **) info->target; +      if (token->type == JSON_TYPE_NULL) { +        *dst = NULL; +      } else { +        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++; +          if (json_unescape(token->ptr, token->len, *dst, unescaped_len) == +              unescaped_len) { +            (*dst)[unescaped_len] = '\0'; +          } else { +            free(*dst); +            *dst = NULL; +          } +        } +      } +      break; +    } +    case 'H': { +#if JSON_ENABLE_HEX +      char **dst = (char **) info->user_data; +      int i, len = token->len / 2; +      *(int *) info->target = len; +      if ((*dst = (char *) malloc(len + 1)) != NULL) { +        for (i = 0; i < len; i++) { +          (*dst)[i] = hexdec(token->ptr + 2 * i); +        } +        (*dst)[len] = '\0'; +        info->num_conversions++; +      } +#endif /* JSON_ENABLE_HEX */ +      break; +    } +    case 'V': { +#if JSON_ENABLE_BASE64 +      char **dst = (char **) info->target; +      int len = token->len * 4 / 3 + 2; +      if ((*dst = (char *) malloc(len + 1)) != NULL) { +        int n = b64dec(token->ptr, token->len, *dst); +        (*dst)[n] = '\0'; +        *(int *) info->user_data = n; +        info->num_conversions++; +      } +#endif /* JSON_ENABLE_BASE64 */ +      break; +    } +    case 'T': +      info->num_conversions++; +      *(struct json_token *) info->target = *token; +      break; +    default: +      if (token->len >= (int) sizeof(buf)) break; +      /* Before converting, copy into tmp buffer in order to 0-terminate it */ +      memcpy(buf, token->ptr, token->len); +      buf[token->len] = '\0'; +      /* NB: Use of base 0 for %d, %ld, %u and %lu is intentional. */ +      if (info->fmt[1] == 'd' || (info->fmt[1] == 'l' && info->fmt[2] == 'd') || +          info->fmt[1] == 'i') { +        char *endptr = NULL; +        long r = strtol(buf, &endptr, 0 /* base */); +        if (*endptr == '\0') { +          if (info->fmt[1] == 'l') { +            *((long *) info->target) = r; +          } else { +            *((int *) info->target) = (int) r; +          } +          info->num_conversions++; +        } +      } else if (info->fmt[1] == 'u' || +                 (info->fmt[1] == 'l' && info->fmt[2] == 'u')) { +        char *endptr = NULL; +        unsigned long r = strtoul(buf, &endptr, 0 /* base */); +        if (*endptr == '\0') { +          if (info->fmt[1] == 'l') { +            *((unsigned long *) info->target) = r; +          } else { +            *((unsigned int *) info->target) = (unsigned int) r; +          } +          info->num_conversions++; +        } +      } else { +#if !JSON_MINIMAL +        info->num_conversions += sscanf(buf, info->fmt, info->target); +#endif +      } +      break; +  } +} + +int json_vscanf(const char *s, int len, const char *fmt, va_list ap) WEAK; +int json_vscanf(const char *s, int len, const char *fmt, va_list ap) { +  char path[JSON_MAX_PATH_LEN] = "", fmtbuf[20]; +  int i = 0; +  char *p = NULL; +  struct json_scanf_info info = {0, path, fmtbuf, NULL, NULL, 0}; + +  while (fmt[i] != '\0') { +    if (fmt[i] == '{') { +      strcat(path, "."); +      i++; +    } else if (fmt[i] == '}') { +      if ((p = strrchr(path, '.')) != NULL) *p = '\0'; +      i++; +    } else if (fmt[i] == '%') { +      info.target = va_arg(ap, void *); +      info.type = fmt[i + 1]; +      switch (fmt[i + 1]) { +        case 'M': +        case 'V': +        case 'H': +          info.user_data = va_arg(ap, void *); +        /* FALLTHROUGH */  | 
