summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--DOCS/man/ipc.rst32
-rw-r--r--misc/json.c36
-rw-r--r--test/json.c16
3 files changed, 77 insertions, 7 deletions
diff --git a/DOCS/man/ipc.rst b/DOCS/man/ipc.rst
index 0c551e8706..2aa406b190 100644
--- a/DOCS/man/ipc.rst
+++ b/DOCS/man/ipc.rst
@@ -74,6 +74,12 @@ some wrapper like .NET's NamedPipeClientStream.)
Protocol
--------
+The protocol uses UTF-8-only JSON as defined by RFC-8259. Unlike standard JSON,
+"\u" escape sequences are not allowed to construct surrogate pairs. To avoid
+getting conflicts, encode all text characters including and above codepoint
+U+0020 as UTF-8. mpv might output broken UTF-8 in corner cases (see "UTF-8"
+section below).
+
Clients can execute commands on the player by sending JSON messages of the
following form:
@@ -266,4 +272,28 @@ sometimes sends invalid JSON. If that is a problem for the client application's
parser, it should filter the raw data for invalid UTF-8 sequences and perform
the desired replacement, before feeding the data to its JSON parser.
-mpv will not attempt to construct invalid UTF-8 with broken escape sequences.
+mpv will not attempt to construct invalid UTF-8 with broken "\u" escape
+sequences. This includes surrogate pairs.
+
+JSON extensions
+---------------
+
+The following non-standard extensions are supported:
+
+ - a list or object item can have a trailing ","
+ - object syntax accepts "=" in addition of ":"
+ - object keys can be unquoted, if they start with a character in "A-Za-z\_"
+ and contain only characters in "A-Za-z0-9\_"
+ - byte escapes with "\xAB" are allowed (with AB being a 2 digit hex number)
+
+Example:
+
+::
+
+ { objkey = "value\x0A" }
+
+Is equivalent to:
+
+::
+
+ { "objkey": "value\n" }
diff --git a/misc/json.c b/misc/json.c
index 9cc0730e4d..d1b2afddb6 100644
--- a/misc/json.c
+++ b/misc/json.c
@@ -22,7 +22,12 @@
* doesn't verify what's passed to strtod(), and also prefers parsing numbers
* as integers with stroll() if possible).
*
- * Does not support extensions like unquoted string literals.
+ * It has some non-standard extensions which shouldn't conflict with JSON:
+ * - a list or object item can have a trailing ","
+ * - object syntax accepts "=" in addition of ":"
+ * - object keys can be unquoted, if they start with a character in [A-Za-z_]
+ * and contain only characters in [A-Za-z0-9_]
+ * - byte escapes with "\xAB" are allowed (with AB being a 2 digit hex number)
*
* Also see: http://tools.ietf.org/html/rfc8259
*
@@ -45,6 +50,7 @@
#include "common/common.h"
#include "misc/bstr.h"
+#include "misc/ctype.h"
#include "json.h"
@@ -72,6 +78,24 @@ void json_skip_whitespace(char **src)
eat_ws(src);
}
+static int read_id(void *ta_parent, struct mpv_node *dst, char **src)
+{
+ char *start = *src;
+ if (!mp_isalpha(**src) && **src != '_')
+ return -1;
+ while (mp_isalnum(**src) || **src == '_')
+ *src += 1;
+ if (**src == ' ') {
+ **src = '\0'; // we're allowed to mutate it => can avoid the strndup
+ *src += 1;
+ } else {
+ start = talloc_strndup(ta_parent, start, *src - start);
+ }
+ dst->format = MPV_FORMAT_STRING;
+ dst->u.string = start;
+ return 0;
+}
+
static int read_str(void *ta_parent, struct mpv_node *dst, char **src)
{
if (!eat_c(src, '"'))
@@ -122,12 +146,18 @@ static int read_sub(void *ta_parent, struct mpv_node *dst, char **src,
if (list->num > 0 && !eat_c(src, ','))
return -1; // missing ','
eat_ws(src);
+ // non-standard extension: allow a trailing ","
+ if (eat_c(src, term))
+ break;
if (is_obj) {
struct mpv_node keynode;
- if (read_str(list, &keynode, src) < 0)
+ // non-standard extension: allow unquoted strings as keys
+ if (read_id(list, &keynode, src) < 0 &&
+ read_str(list, &keynode, src) < 0)
return -1; // key is not a string
eat_ws(src);
- if (!eat_c(src, ':'))
+ // non-standard extension: allow "=" instead of ":"
+ if (!eat_c(src, ':') && !eat_c(src, '='))
return -1; // ':' missing
eat_ws(src);
MP_TARRAY_GROW(list, list->keys, list->num);
diff --git a/test/json.c b/test/json.c
index d624f61cca..0a4462bc21 100644
--- a/test/json.c
+++ b/test/json.c
@@ -45,14 +45,24 @@ static const struct entry entries[] = {
{ "[1,2,3]", "[1,2,3]",
NODE_ARRAY(NODE_INT64(1), NODE_INT64(2), NODE_INT64(3))},
{ "[ ]", "[]", NODE_ARRAY()},
- { "[1,2,]", .expect_fail = true},
{ "[1,,2]", .expect_fail = true},
+ { "[,]", .expect_fail = true},
{ TEXT({"a":1, "b":2}), TEXT({"a":1,"b":2}),
NODE_MAP(L("a", "b"), L(NODE_INT64(1), NODE_INT64(2)))},
{ "{ }", "{}", NODE_MAP(L(), L())},
{ TEXT({"a":b}), .expect_fail = true},
- { TEXT({a:"b"}), .expect_fail = true},
- { TEXT({"a":1,}), .expect_fail = true},
+ { TEXT({1a:"b"}), .expect_fail = true},
+
+ // non-standard extensions
+ { "[1,2,]", "[1,2]", NODE_ARRAY(NODE_INT64(1), NODE_INT64(2))},
+ { TEXT({a:"b"}), TEXT({"a":"b"}),
+ NODE_MAP(L("a"), L(NODE_STR("b")))},
+ { TEXT({a="b"}), TEXT({"a":"b"}),
+ NODE_MAP(L("a"), L(NODE_STR("b")))},
+ { TEXT({a ="b"}), TEXT({"a":"b"}),
+ NODE_MAP(L("a"), L(NODE_STR("b")))},
+ { TEXT({_a12="b"}), TEXT({"_a12":"b"}),
+ NODE_MAP(L("_a12"), L(NODE_STR("b")))},
};
#define MAX_DEPTH 10