summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2017-04-18 05:39:26 +0200
committerwm4 <wm4@nowhere>2017-04-18 05:39:26 +0200
commitfab34a736248f4d1161067871ed2e1f657c4c5a6 (patch)
tree97ed6706d2fcdb624d5bc0d41cbeb3196a9ad1fe
parente335e3323932ca7e27ffbe99e7dc2447a83ba857 (diff)
downloadmpv-property-expand-wip.tar.bz2
mpv-property-expand-wip.tar.xz
introduce new property expand + function syntaxproperty-expand-wip
proof of concept
-rw-r--r--options/m_property.c227
-rw-r--r--options/m_property.h13
2 files changed, 221 insertions, 19 deletions
diff --git a/options/m_property.c b/options/m_property.c
index 9dad34fd1b..c362ed1a32 100644
--- a/options/m_property.c
+++ b/options/m_property.c
@@ -35,6 +35,8 @@
#include "m_property.h"
#include "common/msg.h"
#include "common/common.h"
+#include "misc/ctype.h"
+#include "player/client.h"
struct m_property *m_property_list_find(const struct m_property *list,
const char *name)
@@ -216,7 +218,7 @@ static int m_property_do_bstr(const struct m_property *prop_list, bstr name,
return m_property_do(NULL, prop_list, name0, action, arg, ctx);
}
-static void append_str(char **s, int *len, bstr append)
+static void append_str_old(char **s, int *len, bstr append)
{
MP_TARRAY_GROW(NULL, *s, *len + append.len);
if (append.len)
@@ -249,28 +251,33 @@ static int expand_property(const struct m_property *prop_list, char **ret,
char *append = s;
if (!s && !silent_error && !raw)
append = (r == M_PROPERTY_UNAVAILABLE) ? "(unavailable)" : "(error)";
- append_str(ret, ret_len, bstr0(append));
+ append_str_old(ret, ret_len, bstr0(append));
}
talloc_free(s);
return skip;
}
-char *m_properties_expand_string(const struct m_property *prop_list,
- const char *str0, void *ctx)
+static char *m_legacy_expand(const struct m_property *prop_list,
+ char **str0, void *ctx)
{
char *ret = NULL;
int ret_len = 0;
bool skip = false;
int level = 0, skip_level = 0;
- bstr str = bstr0(str0);
+ bstr str = bstr0(*str0);
+
+ goto start;
while (str.len) {
- if (level > 0 && bstr_eatstart0(&str, "}")) {
+ if (bstr_eatstart0(&str, "}")) {
if (skip && level <= skip_level)
skip = false;
level--;
+ if (level == 0)
+ goto done;
} else if (bstr_startswith0(str, "${") && bstr_find0(str, "}") >= 0) {
str = bstr_cut(str, 2);
+ start:
level++;
// Assume ":" and "}" can't be part of the property name
@@ -287,7 +294,7 @@ char *m_properties_expand_string(const struct m_property *prop_list,
skip_level = level;
}
} else if (level == 0 && bstr_eatstart0(&str, "$>")) {
- append_str(&ret, &ret_len, str);
+ append_str_old(&ret, &ret_len, str);
break;
} else {
char c;
@@ -307,10 +314,216 @@ char *m_properties_expand_string(const struct m_property *prop_list,
}
}
+done:
MP_TARRAY_APPEND(NULL, ret, ret_len, '\0');
+ *str0 = str.start;
return ret;
}
+// Special use within property expansion code. -1 (or all bits set, depending
+// on signedness) is assumed not to be used by any MPV_FORMAT_* constant.
+// If this is set, mpv_node.u.int64 is set to a mp_property_return value.
+#define MP_FORMAT_ERROR ((mpv_format)-1)
+
+// After this, node.format == MPV_FORMAT_STRING, and node.u.string is set.
+static void coerce_string(struct mpv_node *node)
+{
+ switch (node->format) {
+ case MPV_FORMAT_STRING:
+ break;
+ case MP_FORMAT_ERROR: {
+ int r = node->u.int64;
+ char *s = (r == M_PROPERTY_UNAVAILABLE) ? "(unavailable)" : "(error)";
+ node->format = MPV_FORMAT_STRING;
+ node->u.string = talloc_strdup(NULL, s);
+ break;
+ }
+ case MPV_FORMAT_NONE:
+ node->format = MPV_FORMAT_STRING;
+ node->u.string = talloc_strdup(NULL, "");
+ break;
+ default: ;
+ static const struct m_option type = { .type = CONF_TYPE_NODE };
+ char *res = m_option_print(&type, node);
+ assert(res);
+ node->format = MPV_FORMAT_STRING;
+ node->u.string = res;
+ break;
+ }
+
+ assert(node->format == MPV_FORMAT_STRING);
+ assert(node->u.string);
+}
+
+struct expand_ctx {
+ const struct m_property *prop_list;
+ void *ctx;
+};
+
+static void evaluate_fn(struct expand_ctx *ctx, const char *name,
+ struct mpv_node *args, int num_args,
+ struct mpv_node *res)
+{
+ if (strcmp(name, "time") == 0) {
+ if (num_args != 1 || args[0].format != MPV_FORMAT_DOUBLE)
+ goto error;
+ res->format = MPV_FORMAT_STRING;
+ res->u.string = mp_format_time(args[0].u.double_, false);
+ return;
+ } else if (strcmp(name, "precise_time") == 0) {
+ if (num_args != 1 || args[0].format != MPV_FORMAT_DOUBLE)
+ goto error;
+ res->format = MPV_FORMAT_STRING;
+ res->u.string = mp_format_time(args[0].u.double_, true);
+ return;
+ }
+
+error:
+ res->format = MP_FORMAT_ERROR;
+ res->u.int64 = M_PROPERTY_ERROR;
+}
+
+// Force *node to be MPV_FORMAT_STRING, and append s[0..len].
+static void append_str(struct mpv_node *node, char *s, ptrdiff_t len)
+{
+ if (len < 0)
+ len = strlen(s);
+ coerce_string(node);
+ bstr dst = bstr0(node->u.string);
+ bstr_xappend(NULL, &dst, (bstr){s, len});
+ node->u.string = dst.start;
+}
+
+// Return the index of the first character that can't be in a property name.
+static size_t property_len(const char *s)
+{
+ const char *start = s;
+ while (mp_isalnum(s[0]) || s[0] == '/' || s[0] == '_' || s[0] == '-')
+ s++;
+ return s - start;
+}
+
+// Interpret a run of text starting at *str (advances *str to the first
+// character after the end of the run). If top_level==true, don't assume that
+// "," or ")" ends the run. If the run consists of a single function call, and
+// is not top_level, its result is returned verbatim, otherwise everything is
+// converted and concatenated to a string (even on extra whitespace).
+static void expand_text(struct expand_ctx *ctx, char **pstr, bool top_level,
+ struct mpv_node *res)
+{
+ char *str = *pstr;
+ *res = (struct mpv_node){0};
+ char *error = NULL;
+
+ while (str[0]) {
+ // mass-add normal characters
+ size_t len = strcspn(str, top_level ? "$" : ",)$");
+ if (len)
+ append_str(res, str, len);
+ str += len;
+
+ if (str[0] != '$')
+ break;
+ str++;
+
+ // Various escapes
+ if (strchr("$},)", str[0])) {
+ append_str(res, str, 1);
+ str++;
+ continue;
+ } else if (top_level && str[0] == '>') {
+ append_str(res, str, -1);
+ str += strlen(str);
+ continue;
+ }
+
+ if (str[0] == '{') {
+ str++;
+ char *s = m_legacy_expand(ctx->prop_list, &str, ctx->ctx);
+ append_str(res, s, -1);
+ talloc_free(s);
+ continue;
+ }
+
+ // Parse an actual property query or function invocation
+
+ len = property_len(str);
+ if (!len) {
+ error = "property name expected";
+ goto done;
+ }
+
+ char name[64];
+ snprintf(name, sizeof(name), "%.*s", (int)len, str);
+ str += len;
+
+ struct mpv_node val = {0};
+
+ if (str[0] == '(') {
+ // It's a function.
+ str++;
+ void *tmp = talloc_new(NULL);
+ struct mpv_node *args = NULL;
+ int num_args = 0;
+ while (1) {
+ expand_text(ctx, &str, false, &val);
+ MP_TARRAY_APPEND(tmp, args, num_args, val);
+ talloc_steal(tmp, node_get_alloc(&val));
+ if (str[0] == ',') {
+ str++;
+ continue;
+ } else if (str[0] == ')') {
+ str++;
+ break;
+ } else {
+ error = "syntax error in function call";
+ goto done;
+ }
+ }
+
+ evaluate_fn(ctx, name, args, num_args, &val);
+
+ talloc_free(tmp);
+ } else {
+ int r = m_property_do(NULL, ctx->prop_list, name, M_PROPERTY_GET_NODE,
+ &val, ctx->ctx);
+ if (r != M_PROPERTY_OK) {
+ val.format = MP_FORMAT_ERROR;
+ val.u.int64 = r;
+ }
+ }
+
+ if (res->format == MPV_FORMAT_NONE) {
+ *res = val;
+ } else {
+ coerce_string(&val);
+ append_str(res, val.u.string, -1);
+ mpv_free_node_contents(&val);
+ }
+ }
+
+done:
+ if (error) {
+ append_str(res, "(error: ", -1);
+ append_str(res, error, -1);
+ append_str(res, ")", -1);
+ append_str(res, str, -1);
+ str += strlen(str);
+ }
+
+ *pstr = str;
+}
+
+char *m_properties_expand_string(const struct m_property *prop_list,
+ const char *str0, void *ctx)
+{
+ struct expand_ctx ec = {prop_list, ctx};
+ struct mpv_node rnode;
+ expand_text(&ec, (char **)&str0, true, &rnode);
+ coerce_string(&rnode);
+ return rnode.u.string;
+}
+
void m_properties_print_help_list(struct mp_log *log,
const struct m_property *list)
{
diff --git a/options/m_property.h b/options/m_property.h
index d6c8c5aab3..6fe1b495f2 100644
--- a/options/m_property.h
+++ b/options/m_property.h
@@ -154,18 +154,7 @@ bool m_property_split_path(const char *path, bstr *prefix, char **rem);
void m_properties_print_help_list(struct mp_log *log,
const struct m_property *list);
-// Expand a property string.
-// This function allows to print strings containing property values.
-// ${NAME} is expanded to the value of property NAME.
-// If NAME starts with '=', use the raw value of the property.
-// ${NAME:STR} expands to the property, or STR if the property is not
-// available.
-// ${?NAME:STR} expands to STR if the property is available.
-// ${!NAME:STR} expands to STR if the property is not available.
-// General syntax: "${" ["?" | "!"] ["="] NAME ":" STR "}"
-// STR is recursively expanded using the same rules.
-// "$$" can be used to escape "$", and "$}" to escape "}".
-// "$>" disables parsing of "$" for the rest of the string.
+// Expand a property string. Free result with talloc_free().
char* m_properties_expand_string(const struct m_property *prop_list,
const char *str, void *ctx);