Save myMPD settings in state file, not in cookies

This commit is contained in:
jcorporation
2018-06-21 17:57:40 +01:00
parent b790a687a3
commit c11bfa0d1a
9 changed files with 716 additions and 391 deletions
+491 -75
View File
@@ -1,20 +1,19 @@
/*
* Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
* Copyright (c) 2013 Cesanta Software Limited
* Copyright (c) 2018 Cesanta Software Limited
* All rights reserved
*
* This library is dual-licensed: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. For the terms of this
* license, see <http: *www.gnu.org/licenses/>.
* 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
*
* You are free to use this library under the terms of the GNU General
* Public License, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
* http://www.apache.org/licenses/LICENSE-2.0
*
* Alternatively, you can license this library under a commercial
* license, as set out in <http://cesanta.com/products.html>.
* 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+ */
@@ -35,6 +34,8 @@
#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, ...);
@@ -47,33 +48,27 @@ typedef unsigned _int64 uint64_t;
#endif
#define PRId64 "I64d"
#define PRIu64 "I64u"
#if !defined(SIZE_T_FMT)
#if _MSC_VER >= 1310
#define SIZE_T_FMT "Iu"
#else
#define SIZE_T_FMT "u"
#endif
#endif
#else /* _WIN32 */
/* <inttypes.h> wants this for C++ */
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif
#include <inttypes.h>
#if !defined(SIZE_T_FMT)
#define SIZE_T_FMT "zu"
#endif
#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_MAX_PATH_LEN
#define JSON_MAX_PATH_LEN 60
#define JSON_MAX_PATH_LEN 256
#endif
struct frozen {
@@ -85,14 +80,14 @@ struct frozen {
/* For callback API */
char path[JSON_MAX_PATH_LEN];
int path_len;
size_t path_len;
void *callback_data;
json_walk_callback_t callback;
};
struct fstate {
const char *ptr;
int path_len;
size_t path_len;
};
#define SET_STATE(fr, ptr, str, len) \
@@ -117,14 +112,15 @@ struct fstate {
static int append_to_path(struct frozen *f, const char *str, int size) {
int n = f->path_len;
f->path_len +=
snprintf(f->path + f->path_len, sizeof(f->path) - (f->path_len + 1),
"%.*s", size, str);
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 truncate_path(struct frozen *f, int len) {
static void truncate_path(struct frozen *f, size_t len) {
f->path_len = len;
f->path[len] = '\0';
}
@@ -242,7 +238,7 @@ static int parse_string(struct frozen *f) {
ch = *(unsigned char *) f->cur;
len = get_utf8_char_len((unsigned char) ch);
EXPECT(ch >= 32 && len > 0, JSON_STRING_INVALID); /* No control chars */
EXPECT(len < left(f), JSON_STRING_INCOMPLETE);
EXPECT(len <= left(f), JSON_STRING_INCOMPLETE);
if (ch == '\\') {
EXPECT((n = get_escape_len(f->cur + 1, left(f))) > 0, n);
len += n;
@@ -288,9 +284,9 @@ static int parse_number(struct frozen *f) {
static int parse_array(struct frozen *f) {
int i = 0, current_path_len;
char buf[20];
CALL_BACK(f, JSON_TYPE_ARRAY_START, NULL, 0);
TRY(test_and_skip(f, '['));
{
CALL_BACK(f, JSON_TYPE_ARRAY_START, NULL, 0);
{
SET_STATE(f, f->cur - 1, "", 0);
while (cur(f) != ']') {
@@ -374,9 +370,6 @@ static int parse_value(struct frozen *f) {
/* key = identifier | string */
static int parse_key(struct frozen *f) {
int ch = cur(f);
#if 0
printf("%s [%.*s]\n", __func__, (int) (f->end - f->cur), f->cur);
#endif
if (is_alpha(ch)) {
TRY(parse_identifier(f));
} else if (ch == '"') {
@@ -407,19 +400,17 @@ static int parse_pair(struct frozen *f) {
/* object = '{' pair { ',' pair } '}' */
static int parse_object(struct frozen *f) {
CALL_BACK(f, JSON_TYPE_OBJECT_START, NULL, 0);
TRY(test_and_skip(f, '{'));
{
CALL_BACK(f, JSON_TYPE_OBJECT_START, NULL, 0);
{
SET_STATE(f, f->cur - 1, ".", 1);
while (cur(f) != '}') {
TRY(parse_pair(f));
if (cur(f) == ',') f->cur++;
}
TRY(test_and_skip(f, '}'));
truncate_path(f, fstate.path_len);
CALL_BACK(f, JSON_TYPE_OBJECT_END, fstate.ptr, f->cur - fstate.ptr);
SET_STATE(f, f->cur - 1, ".", 1);
while (cur(f) != '}') {
TRY(parse_pair(f));
if (cur(f) == ',') f->cur++;
}
TRY(test_and_skip(f, '}'));
truncate_path(f, fstate.path_len);
CALL_BACK(f, JSON_TYPE_OBJECT_END, fstate.ptr, f->cur - fstate.ptr);
}
return 0;
}
@@ -430,7 +421,8 @@ static int doit(struct frozen *f) {
return parse_value(f);
}
static int json_encode_string(struct json_out *out, const char *p, size_t len) {
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";
@@ -505,7 +497,7 @@ static int b64rev(int c) {
}
}
static uint8_t hexdec(const char *s) {
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));
@@ -569,7 +561,7 @@ int json_vprintf(struct json_out *out, const char *fmt, va_list xap) {
skip += 2;
} else if (fmt[1] == 'z' && fmt[2] == 'u') {
size_t val = va_arg(ap, size_t);
snprintf(buf, sizeof(buf), "%" SIZE_T_FMT, val);
snprintf(buf, sizeof(buf), "%lu", (unsigned long) val);
len += out->printer(out, buf, strlen(buf));
skip += 1;
} else if (fmt[1] == 'M') {
@@ -613,7 +605,7 @@ int json_vprintf(struct json_out *out, const char *fmt, va_list xap) {
l = strlen(p);
}
len += out->printer(out, quote, 1);
len += json_encode_string(out, p, l);
len += json_escape(out, p, l);
len += out->printer(out, quote, 1);
}
} else {
@@ -628,30 +620,48 @@ int json_vprintf(struct json_out *out, const char *fmt, va_list xap) {
* TODO(dfrank): reimplement %s and %.*s in order to avoid that.
*/
const char *end_of_format_specifier = "sdfFgGlhuI.*-0123456789";
size_t n = strspn(fmt + 1, end_of_format_specifier);
const char *end_of_format_specifier = "sdfFgGlhuIcx.*-0123456789";
int n = strspn(fmt + 1, end_of_format_specifier);
char *pbuf = buf;
size_t need_len;
int need_len, size = sizeof(buf);
char fmt2[20];
va_list sub_ap;
strncpy(fmt2, fmt, n + 1 > sizeof(fmt2) ? sizeof(fmt2) : n + 1);
va_list ap_copy;
strncpy(fmt2, fmt,
n + 1 > (int) sizeof(fmt2) ? sizeof(fmt2) : (size_t) n + 1);
fmt2[n + 1] = '\0';
va_copy(sub_ap, ap);
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.
*/
if (need_len > sizeof(buf)) {
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
*/
pbuf = (char *) malloc(need_len);
va_copy(sub_ap, ap);
vsnprintf(pbuf, need_len, fmt2, sub_ap);
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;
}
/*
@@ -663,7 +673,6 @@ int json_vprintf(struct json_out *out, const char *fmt, va_list xap) {
if ((n + 1 == strlen("%" PRId64) && strcmp(fmt2, "%" PRId64) == 0) ||
(n + 1 == strlen("%" PRIu64) && strcmp(fmt2, "%" PRIu64) == 0)) {
(void) va_arg(ap, int64_t);
skip += strlen(PRIu64) - 1;
} else if (strcmp(fmt2, "%.*s") == 0) {
(void) va_arg(ap, int);
(void) va_arg(ap, char *);
@@ -704,6 +713,7 @@ int json_vprintf(struct json_out *out, const char *fmt, va_list xap) {
}
len += out->printer(out, quote, 1);
} else {
len += out->printer(out, fmt, 1);
fmt++;
}
}
@@ -749,7 +759,8 @@ int json_printf_array(struct json_out *out, va_list *ap) {
}
#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) 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);
@@ -788,6 +799,7 @@ int json_walk(const char *json_string, int json_string_length,
}
struct scan_array_info {
int found;
char path[JSON_MAX_PATH_LEN];
struct json_token *token;
};
@@ -802,6 +814,7 @@ static void json_scanf_array_elem_cb(void *callback_data, const char *name,
if (strcmp(path, info->path) == 0) {
*info->token = *token;
info->found = 1;
}
}
@@ -811,10 +824,11 @@ 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 token->len;
return info.found ? token->len : -1;
}
struct json_scanf_info {
@@ -856,6 +870,7 @@ 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;
@@ -876,7 +891,17 @@ static void json_scanf_cb(void *callback_data, const char *name,
switch (info->type) {
case 'B':
info->num_conversions++;
*(int *) info->target = (token->type == JSON_TYPE_TRUE ? 1 : 0);
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 {
@@ -889,12 +914,21 @@ static void json_scanf_cb(void *callback_data, const char *name,
}
case 'Q': {
char **dst = (char **) info->target;
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';
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;
}
@@ -927,7 +961,12 @@ static void json_scanf_cb(void *callback_data, const char *name,
*(struct json_token *) info->target = *token;
break;
default:
info->num_conversions += sscanf(token->ptr, info->fmt, info->target);
/* Before scanf, copy into tmp buffer in order to 0-terminate it */
if (token->len < (int) sizeof(buf)) {
memcpy(buf, token->ptr, token->len);
buf[token->len] = '\0';
info->num_conversions += sscanf(buf, info->fmt, info->target);
}
break;
}
}
@@ -992,3 +1031,380 @@ int json_scanf(const char *str, int len, const char *fmt, ...) {
va_end(ap);
return result;
}
int json_vfprintf(const char *file_name, const char *fmt, va_list ap) WEAK;
int json_vfprintf(const char *file_name, const char *fmt, va_list ap) {
int res = -1;
FILE *fp = fopen(file_name, "wb");
if (fp != NULL) {
struct json_out out = JSON_OUT_FILE(fp);
res = json_vprintf(&out, fmt, ap);
fputc('\n', fp);
fclose(fp);
}
return res;
}
int json_fprintf(const char *file_name, const char *fmt, ...) WEAK;
int json_fprintf(const char *file_name, const char *fmt, ...) {
int result;
va_list ap;
va_start(ap, fmt);
result = json_vfprintf(file_name, fmt, ap);
va_end(ap);
return result;
}
char *json_fread(const char *path) WEAK;
char *json_fread(const char *path) {
FILE *fp;
char *data = NULL;
if ((fp = fopen(path, "rb")) == NULL) {
} else if (fseek(fp, 0, SEEK_END) != 0) {
fclose(fp);
} else {
long size = ftell(fp);
if (size > 0 && (data = (char *) malloc(size + 1)) != NULL) {
fseek(fp, 0, SEEK_SET); /* Some platforms might not have rewind(), Oo */
if (fread(data, 1, size, fp) != (size_t) size) {
free(data);
data = NULL;
} else {
data[size] = '\0';
}
}
fclose(fp);
}
return data;
}
struct json_setf_data {
const char *json_path;
const char *base; /* Pointer to the source JSON string */
int matched; /* Matched part of json_path */
int pos; /* Offset of the mutated value begin */
int end; /* Offset of the mutated value end */
int prev; /* Offset of the previous token end */
};
static int get_matched_prefix_len(const char *s1, const char *s2) {
int i = 0;
while (s1[i] && s2[i] && s1[i] == s2[i]) i++;
return i;
}
static void json_vsetf_cb(void *userdata, const char *name, size_t name_len,
const char *path, const struct json_token *t) {
struct json_setf_data *data = (struct json_setf_data *) userdata;
int off, len = get_matched_prefix_len(path, data->json_path);
if (t->ptr == NULL) return;
off = t->ptr - data->base;
// printf("--%d %s %d\n", t->type, path, off);
if (len > data->matched) data->matched = len;
/*
* If there is no exact path match, set the mutation position to tbe end
* of the object or array
*/
if (len < data->matched && data->pos == 0 &&
(t->type == JSON_TYPE_OBJECT_END || t->type == JSON_TYPE_ARRAY_END)) {
data->pos = data->end = data->prev;
}
/* Exact path match. Set mutation position to the value of this token */
if (strcmp(path, data->json_path) == 0 && t->type != JSON_TYPE_OBJECT_START &&
t->type != JSON_TYPE_ARRAY_START) {
data->pos = off;
data->end = off + t->len;
}
/*
* For deletion, we need to know where the previous value ends, because
* we don't know where matched value key starts.
* When the mutation position is not yet set, remember each value end.
* When the mutation position is already set, but it is at the beginning
* of the object/array, we catch the end of the object/array and see
* whether the object/array start is closer then previously stored prev.
*/
if (data->pos == 0) {
data->prev = off + t->len; /* pos is not yet set */
} else if ((t->ptr[0] == '[' || t->ptr[0] == '{') && off + 1 < data->pos &&
off + 1 > data->prev) {
data->prev = off + 1;
}
(void) name;
(void) name_len;
}
int json_vsetf(const char *s, int len, struct json_out *out,
const char *json_path, const char *json_fmt, va_list ap) WEAK;
int json_vsetf(const char *s, int len, struct json_out *out,
const char *json_path, const char *json_fmt, va_list ap) {
struct json_setf_data data;
memset(&data, 0, sizeof(data));
data.json_path = json_path;
data.base = s;
data.end = len;
// printf("S:[%.*s] %s %p\n", len, s, json_path, json_fmt);
json_walk(s, len, json_vsetf_cb, &data);
// printf("-> %d %d %d\n", data.prev, data.pos, data.end);
if (json_fmt == NULL) {
/* Deletion codepath */
json_printf(out, "%.*s", data.prev, s);
/* Trim comma after the value that begins at object/array start */
if (s[data.prev - 1] == '{' || s[data.prev - 1] == '[') {
int i = data.end;
while (i < len && is_space(s[i])) i++;
if (s[i] == ',') data.end = i + 1; /* Point after comma */
}
json_printf(out, "%.*s", len - data.end, s + data.end);
} else {
/* Modification codepath */
int n, off = data.matched, depth = 0;
/* Print the unchanged beginning */
json_printf(out, "%.*s", data.pos, s);
/* Add missing keys */
while ((n = strcspn(&json_path[off], ".[")) > 0) {
if (s[data.prev - 1] != '{' && s[data.prev - 1] != '[' && depth == 0) {
json_printf(out, ",");
}
if (off > 0 && json_path[off - 1] != '.') break;
json_printf(out, "%.*Q:", 1, json_path + off);
off += n;
if (json_path[off] != '\0') {
json_printf(out, "%c", json_path[off] == '.' ? '{' : '[');
depth++;
off++;
}
}
/* Print the new value */
json_vprintf(out, json_fmt, ap);
/* Close brackets/braces of the added missing keys */
for (; off > data.matched; off--) {
int ch = json_path[off];
const char *p = ch == '.' ? "}" : ch == '[' ? "]" : "";
json_printf(out, "%s", p);
}
/* Print the rest of the unchanged string */
json_printf(out, "%.*s", len - data.end, s + data.end);
}
return data.end > data.pos ? 1 : 0;
}
int json_setf(const char *s, int len, struct json_out *out,
const char *json_path, const char *json_fmt, ...) WEAK;
int json_setf(const char *s, int len, struct json_out *out,
const char *json_path, const char *json_fmt, ...) {
int result;
va_list ap;
va_start(ap, json_fmt);
result = json_vsetf(s, len, out, json_path, json_fmt, ap);
va_end(ap);
return result;
}
struct prettify_data {
struct json_out *out;
int level;
int last_token;
};
static void indent(struct json_out *out, int level) {
while (level-- > 0) out->printer(out, " ", 2);
}
static void print_key(struct prettify_data *pd, const char *path,
const char *name, int name_len) {
if (pd->last_token != JSON_TYPE_INVALID &&
pd->last_token != JSON_TYPE_ARRAY_START &&
pd->last_token != JSON_TYPE_OBJECT_START) {
pd->out->printer(pd->out, ",", 1);
}
if (path[0] != '\0') pd->out->printer(pd->out, "\n", 1);
indent(pd->out, pd->level);
if (path[0] != '\0' && path[strlen(path) - 1] != ']') {
pd->out->printer(pd->out, "\"", 1);
pd->out->printer(pd->out, name, (int) name_len);
pd->out->printer(pd->out, "\"", 1);
pd->out->printer(pd->out, ": ", 2);
}
}
static void prettify_cb(void *userdata, const char *name, size_t name_len,
const char *path, const struct json_token *t) {
struct prettify_data *pd = (struct prettify_data *) userdata;
switch (t->type) {
case JSON_TYPE_OBJECT_START:
case JSON_TYPE_ARRAY_START:
print_key(pd, path, name, name_len);
pd->out->printer(pd->out, t->type == JSON_TYPE_ARRAY_START ? "[" : "{",
1);
pd->level++;
break;
case JSON_TYPE_OBJECT_END:
case JSON_TYPE_ARRAY_END:
pd->level--;
if (pd->last_token != JSON_TYPE_INVALID &&
pd->last_token != JSON_TYPE_ARRAY_START &&
pd->last_token != JSON_TYPE_OBJECT_START) {
pd->out->printer(pd->out, "\n", 1);
indent(pd->out, pd->level);
}
pd->out->printer(pd->out, t->type == JSON_TYPE_ARRAY_END ? "]" : "}", 1);
break;
case JSON_TYPE_NUMBER:
case JSON_TYPE_NULL:
case JSON_TYPE_TRUE:
case JSON_TYPE_FALSE:
case JSON_TYPE_STRING:
print_key(pd, path, name, name_len);
if (t->type == JSON_TYPE_STRING) pd->out->printer(pd->out, "\"", 1);
pd->out->printer(pd->out, t->ptr, t->len);
if (t->type == JSON_TYPE_STRING) pd->out->printer(pd->out, "\"", 1);
break;
default:
break;
}
pd->last_token = t->type;
}
int json_prettify(const char *s, int len, struct json_out *out) WEAK;
int json_prettify(const char *s, int len, struct json_out *out) {
struct prettify_data pd = {out, 0, JSON_TYPE_INVALID};
return json_walk(s, len, prettify_cb, &pd);
}
int json_prettify_file(const char *file_name) WEAK;
int json_prettify_file(const char *file_name) {
int res = -1;
char *s = json_fread(file_name);
FILE *fp;
if (s != NULL && (fp = fopen(file_name, "wb")) != NULL) {
struct json_out out = JSON_OUT_FILE(fp);
res = json_prettify(s, strlen(s), &out);
if (res < 0) {
/* On error, restore the old content */
fclose(fp);
fp = fopen(file_name, "wb");
fseek(fp, 0, SEEK_SET);
fwrite(s, 1, strlen(s), fp);
} else {
fputc('\n', fp);
}
fclose(fp);
}
free(s);
return res;
}
struct next_data {
void *handle; // Passed handle. Changed if a next entry is found
const char *path; // Path to the iterated object/array
int path_len; // Path length - optimisation
int found; // Non-0 if found the next entry
struct json_token *key; // Object's key
struct json_token *val; // Object's value
int *idx; // Array index
};
static void next_set_key(struct next_data *d, const char *name, int name_len,
int is_array) {
if (is_array) {
/* Array. Set index and reset key */
if (d->key != NULL) {
d->key->len = 0;
d->key->ptr = NULL;
}
if (d->idx != NULL) *d->idx = atoi(name);
} else {
/* Object. Set key and make index -1 */
if (d->key != NULL) {
d->key->ptr = name;
d->key->len = name_len;
}
if (d->idx != NULL) *d->idx = -1;
}
}
static void next_cb(void *userdata, const char *name, size_t name_len,
const char *path, const struct json_token *t) {
struct next_data *d = (struct next_data *) userdata;
const char *p = path + d->path_len;
if (d->found) return;
if (d->path_len >= (int) strlen(path)) return;
if (strncmp(d->path, path, d->path_len) != 0) return;
if (strchr(p + 1, '.') != NULL) return; /* More nested objects - skip */
if (strchr(p + 1, '[') != NULL) return; /* Ditto for arrays */
// {OBJECT,ARRAY}_END types do not pass name, _START does. Save key.
if (t->type == JSON_TYPE_OBJECT_START || t->type == JSON_TYPE_ARRAY_START) {
// printf("SAV %s %d %p\n", path, t->type, t->ptr);
next_set_key(d, name, name_len, p[0] == '[');
} else if (d->handle == NULL || d->handle < (void *) t->ptr) {
// printf("END %s %d %p\n", path, t->type, t->ptr);
if (t->type != JSON_TYPE_OBJECT_END && t->type != JSON_TYPE_ARRAY_END) {
next_set_key(d, name, name_len, p[0] == '[');
}
if (d->val != NULL) *d->val = *t;
d->handle = (void *) t->ptr;
d->found = 1;
}
}
static void *json_next(const char *s, int len, void *handle, const char *path,
struct json_token *key, struct json_token *val, int *i) {
struct json_token tmpval, *v = val == NULL ? &tmpval : val;
struct json_token tmpkey, *k = key == NULL ? &tmpkey : key;
int tmpidx, *pidx = i == NULL ? &tmpidx : i;
struct next_data data = {handle, path, strlen(path), 0, k, v, pidx};
json_walk(s, len, next_cb, &data);
return data.found ? data.handle : NULL;
}
void *json_next_key(const char *s, int len, void *handle, const char *path,
struct json_token *key, struct json_token *val) WEAK;
void *json_next_key(const char *s, int len, void *handle, const char *path,
struct json_token *key, struct json_token *val) {
return json_next(s, len, handle, path, key, val, NULL);
}
void *json_next_elem(const char *s, int len, void *handle, const char *path,
int *idx, struct json_token *val) WEAK;
void *json_next_elem(const char *s, int len, void *handle, const char *path,
int *idx, struct json_token *val) {
return json_next(s, len, handle, path, NULL, val, idx);
}
static int json_sprinter(struct json_out *out, const char *str, size_t len) {
size_t old_len = out->u.buf.buf == NULL ? 0 : strlen(out->u.buf.buf);
size_t new_len = len + old_len;
char *p = (char *) realloc(out->u.buf.buf, new_len + 1);
if (p != NULL) {
memcpy(p + old_len, str, len);
p[new_len] = '\0';
out->u.buf.buf = p;
}
return len;
}
char *json_vasprintf(const char *fmt, va_list ap) WEAK;
char *json_vasprintf(const char *fmt, va_list ap) {
struct json_out out;
memset(&out, 0, sizeof(out));
out.printer = json_sprinter;
json_vprintf(&out, fmt, ap);
return out.u.buf.buf;
}
char *json_asprintf(const char *fmt, ...) WEAK;
char *json_asprintf(const char *fmt, ...) {
char *result = NULL;
va_list ap;
va_start(ap, fmt);
result = json_vasprintf(fmt, ap);
va_end(ap);
return result;
}
+114 -15
View File
@@ -1,20 +1,19 @@
/*
* Copyright (c) 2004-2013 Sergey Lyubka <valenok@gmail.com>
* Copyright (c) 2013 Cesanta Software Limited
* Copyright (c) 2018 Cesanta Software Limited
* All rights reserved
*
* This library is dual-licensed: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. For the terms of this
* license, see <http: *www.gnu.org/licenses/>.
* 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
*
* You are free to use this library under the terms of the GNU General
* Public License, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
* http://www.apache.org/licenses/LICENSE-2.0
*
* Alternatively, you can license this library under a commercial
* license, as set out in <http://cesanta.com/products.html>.
* 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.
*/
#ifndef CS_FROZEN_FROZEN_H_
@@ -28,6 +27,13 @@ extern "C" {
#include <stddef.h>
#include <stdio.h>
#if defined(_WIN32) && _MSC_VER < 1700
typedef int bool;
enum { false = 0, true = 1 };
#else
#include <stdbool.h>
#endif
/* JSON token type */
enum json_token_type {
JSON_TYPE_INVALID = 0, /* memsetting to 0 should create INVALID value */
@@ -94,6 +100,7 @@ typedef void (*json_walk_callback_t)(void *callback_data, const char *name,
/*
* Parse `json_string`, invoking `callback` in a way similar to SAX parsers;
* see `json_walk_callback_t`.
* Return number of processed bytes, or a negative error code.
*/
int json_walk(const char *json_string, int json_string_length,
json_walk_callback_t callback, void *callback_data);
@@ -127,7 +134,7 @@ extern int json_printer_file(struct json_out *, const char *, size_t);
#define JSON_OUT_FILE(fp) \
{ \
json_printer_file, { \
{ (void *) fp, 0, 0 } \
{ (char *) fp, 0, 0 } \
} \
}
@@ -144,13 +151,34 @@ typedef int (*json_printf_callback_t)(struct json_out *, va_list *ap);
* - `%M` invokes a json_printf_callback_t function. That callback function
* can consume more parameters.
*
* Return number of bytes printed. If the return value is bigger then the
* Return number of bytes printed. If the return value is bigger than the
* supplied buffer, that is an indicator of overflow. In the overflow case,
* overflown bytes are not printed.
*/
int json_printf(struct json_out *, const char *fmt, ...);
int json_vprintf(struct json_out *, const char *fmt, va_list ap);
/*
* Same as json_printf, but prints to a file.
* File is created if does not exist. File is truncated if already exists.
*/
int json_fprintf(const char *file_name, const char *fmt, ...);
int json_vfprintf(const char *file_name, const char *fmt, va_list ap);
/*
* Print JSON into an allocated 0-terminated string.
* Return allocated string, or NULL on error.
* Example:
*
* ```c
* char *str = json_asprintf("{a:%H}", 3, "abc");
* printf("%s\n", str); // Prints "616263"
* free(str);
* ```
*/
char *json_asprintf(const char *fmt, ...);
char *json_vasprintf(const char *fmt, va_list ap);
/*
* Helper %M callback that prints contiguous C arrays.
* Consumes void *array_ptr, size_t array_size, size_t elem_size, char *fmt
@@ -165,7 +193,8 @@ int json_printf_array(struct json_out *, va_list *ap);
* 1. Object keys in the format string may be not quoted, e.g. "{key: %d}"
* 2. Order of keys in an object is irrelevant.
* 3. Several extra format specifiers are supported:
* - %B: consumes `int *`, expects boolean `true` or `false`.
* - %B: consumes `int *` (or `char *`, if `sizeof(bool) == sizeof(char)`),
* expects boolean `true` or `false`.
* - %Q: consumes `char **`, expects quoted, JSON-encoded string. Scanned
* string is malloc-ed, caller must free() the string.
* - %V: consumes `char **`, `int *`. Expects base64-encoded string.
@@ -193,7 +222,7 @@ typedef void (*json_scanner_t)(const char *str, int len, void *user_data);
/*
* Helper function to scan array item with given path and index.
* Fills `token` with the matched JSON token.
* Return 0 if no array element found, otherwise non-0.
* Return -1 if no array element found, otherwise non-negative token length.
*/
int json_scanf_array_elem(const char *s, int len, const char *path, int index,
struct json_token *token);
@@ -207,6 +236,76 @@ int json_scanf_array_elem(const char *s, int len, const char *path, int index,
*/
int json_unescape(const char *src, int slen, char *dst, int dlen);
/*
* Escape a string `str`, `str_len` into the printer `out`.
* Return the number of bytes printed.
*/
int json_escape(struct json_out *out, const char *str, size_t str_len);
/*
* Read the whole file in memory.
* Return malloc-ed file content, or NULL on error. The caller must free().
*/
char *json_fread(const char *file_name);
/*
* Update given JSON string `s,len` by changing the value at given `json_path`.
* The result is saved to `out`. If `json_fmt` == NULL, that deletes the key.
* If path is not present, missing keys are added. Array path without an
* index pushes a value to the end of an array.
* Return 1 if the string was changed, 0 otherwise.
*
* Example: s is a JSON string { "a": 1, "b": [ 2 ] }
* json_setf(s, len, out, ".a", "7"); // { "a": 7, "b": [ 2 ] }
* json_setf(s, len, out, ".b", "7"); // { "a": 1, "b": 7 }
* json_setf(s, len, out, ".b[]", "7"); // { "a": 1, "b": [ 2,7 ] }
* json_setf(s, len, out, ".b", NULL); // { "a": 1 }
*/
int json_setf(const char *s, int len, struct json_out *out,
const char *json_path, const char *json_fmt, ...);
int json_vsetf(const char *s, int len, struct json_out *out,
const char *json_path, const char *json_fmt, va_list ap);
/*
* Pretty-print JSON string `s,len` into `out`.
* Return number of processed bytes in `s`.
*/
int json_prettify(const char *s, int len, struct json_out *out);
/*
* Prettify JSON file `file_name`.
* Return number of processed bytes, or negative number of error.
* On error, file content is not modified.
*/
int json_prettify_file(const char *file_name);
/*
* Iterate over an object at given JSON `path`.
* On each iteration, fill the `key` and `val` tokens. It is OK to pass NULL
* for `key`, or `val`, in which case they won't be populated.
* Return an opaque value suitable for the next iteration, or NULL when done.
*
* Example:
*
* ```c
* void *h = NULL;
* struct json_token key, val;
* while ((h = json_next_key(s, len, h, ".foo", &key, &val)) != NULL) {
* printf("[%.*s] -> [%.*s]\n", key.len, key.ptr, val.len, val.ptr);
* }
* ```
*/
void *json_next_key(const char *s, int len, void *handle, const char *path,
struct json_token *key, struct json_token *val);
/*
* Iterate over an array at given JSON `path`.
* Similar to `json_next_key`, but fills array index `idx` instead of `key`.
*/
void *json_next_elem(const char *s, int len, void *handle, const char *path,
int *idx, struct json_token *val);
#ifdef __cplusplus
}
#endif /* __cplusplus */
+21 -24
View File
@@ -59,8 +59,8 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg)
int je, int_buf;
float float_buf;
char *p_charbuf1, *p_charbuf2;
FILE *fp;
struct mympd_state { int a; int b; } state = { .a = 0, .b = 0 };
#ifdef DEBUG
fprintf(stdout,"Got request: %s\n",msg.p);
#endif
@@ -72,24 +72,10 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg)
return;
switch(cmd_id) {
case MPD_API_SET_MYMPD_SETTINGS:
fp = fopen(mpd.statefile,"w");
if(fp != NULL) {
fprintf(fp,"%.*s",msg.len,msg.p);
fclose(fp);
} else {
fprintf(stderr,"Cant write state file\n");
}
break;
case MPD_API_GET_MYMPD_SETTINGS:
fp = fopen(mpd.statefile,"r");
if(fp != NULL) {
fgets(mpd.buf, MAX_SIZE, fp);
fclose(fp);
n=strlen(mpd.buf);
}
break;
case MPD_API_SET_MPD_SETTINGS:
case MPD_API_SET_SETTINGS:
json_scanf(msg.p, msg.len, "{ data: { notificationWeb: %d, notificationPage: %d} }", &state.a, &state.b);
json_fprintf(mpd.statefile, "{ notificationWeb: %d, notificationPage: %d}", state.a, state.b);
je = json_scanf(msg.p, msg.len, "{ data: { random:%u } }", &uint_buf1);
if (je == 1)
mpd_run_random(mpd.conn, uint_buf1);
@@ -305,7 +291,7 @@ void callback_mympd(struct mg_connection *nc, const struct mg_str msg)
mpd_run_rm(mpd.conn, p_charbuf1);
free(p_charbuf1);
break;
case MPD_API_GET_MPD_SETTINGS:
case MPD_API_GET_SETTINGS:
n = mympd_put_settings(mpd.buf);
break;
case MPD_API_GET_STATS:
@@ -630,6 +616,14 @@ int mympd_put_settings(char *buffer)
char *replaygain;
int len;
struct json_out out = JSON_OUT_BUF(buffer, MAX_SIZE);
struct mympd_state { int a; int b; } state = { .a = 0, .b = 0 };
if( access( mpd.statefile, F_OK ) != -1 ) {
char *content = json_fread(mpd.statefile);
json_scanf(content, strlen(content), "{notificationWeb: %d, notificationPage: %d}", &state.a, &state.b);
} else {
state.a=0;
state.b=0;
}
status = mpd_run_status(mpd.conn);
if (!status) {
@@ -644,12 +638,13 @@ int mympd_put_settings(char *buffer)
replaygain=strdup(pair->value);
mpd_return_pair(mpd.conn, pair);
}
len = json_printf(&out,
"{type:settings, data:{"
"repeat:%d, single:%d, crossfade:%d, consume:%d, random:%d, "
"mixrampdb: %f, mixrampdelay: %f, mpdhost : %Q, mpdport: %d, passwort_set: %B, "
"streamport: %d, coverimage: %Q, max_elements_per_page: %d, replaygain: %Q"
"streamport: %d, coverimage: %Q, max_elements_per_page: %d, replaygain: %Q,"
"notificationWeb: %d, notificationPage: %d"
"}}",
mpd_status_get_repeat(status),
mpd_status_get_single(status),
@@ -662,7 +657,9 @@ int mympd_put_settings(char *buffer)
mpd.password ? "true" : "false",
streamport, coverimage,
MAX_ELEMENTS_PER_PAGE,
replaygain
replaygain,
state.a,
state.b
);
mpd_status_free(status);
+2 -4
View File
@@ -78,10 +78,8 @@
X(MPD_API_GET_ARTISTS) \
X(MPD_API_GET_CURRENT_SONG) \
X(MPD_API_WELCOME) \
X(MPD_API_GET_MPD_SETTINGS) \
X(MPD_API_SET_MPD_SETTINGS) \
X(MPD_API_SET_MYMPD_SETTINGS) \
X(MPD_API_GET_MYMPD_SETTINGS)
X(MPD_API_GET_SETTINGS) \
X(MPD_API_SET_SETTINGS)
enum mpd_cmd_ids {
MPD_CMDS(GEN_ENUM)