From 63903c27bd9947b94ef83dc350568b5797af5281 Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 10 Oct 2014 22:37:11 +0200 Subject: input: add a function to parse mpv_node as command For future client API enhancements. --- input/cmd_parse.c | 222 +++++++++++++++++++++++++++++++++++++++--------------- input/cmd_parse.h | 2 + 2 files changed, 162 insertions(+), 62 deletions(-) diff --git a/input/cmd_parse.c b/input/cmd_parse.c index fd45094105..b9fd494914 100644 --- a/input/cmd_parse.c +++ b/input/cmd_parse.c @@ -27,6 +27,8 @@ #include "cmd_list.h" #include "input.h" +#include "libmpv/client.h" + static void destroy_cmd(void *ptr) { struct mp_cmd *cmd = ptr; @@ -118,10 +120,152 @@ static const struct flag cmd_flags[] = { {0} }; +static bool apply_flag(struct mp_cmd *cmd, bstr str) +{ + for (int n = 0; cmd_flags[n].name; n++) { + if (bstr_equals0(str, cmd_flags[n].name)) { + cmd->flags = (cmd->flags & ~cmd_flags[n].remove) | cmd_flags[n].add; + return true; + } + } + return false; +} + +static bool find_cmd(struct mp_log *log, struct mp_cmd *cmd, bstr name) +{ + if (name.len == 0) { + mp_err(log, "Command name missing.\n"); + return false; + } + for (int n = 0; mp_cmds[n].name; n++) { + if (bstr_equals0(name, mp_cmds[n].name)) { + cmd->def = &mp_cmds[n]; + cmd->name = (char *)cmd->def->name; + cmd->id = cmd->def->id; + return true; + } + } + mp_err(log, "Command '%.*s' not found.\n", BSTR_P(name)); + return false; +} + +static bool is_vararg(const struct mp_cmd_def *m, int i) +{ + return m->vararg && (i + 1 >= MP_CMD_MAX_ARGS || !m->args[i + 1].type); +} + +static const struct m_option *get_arg_type(const struct mp_cmd_def *cmd, int i) +{ + if (i >= MP_CMD_MAX_ARGS) + return NULL; + const struct m_option *opt = &cmd->args[i]; + if (!opt->type && is_vararg(cmd, i)) { + // The last arg in a vararg command sets all vararg types. + for (int n = i; n >= 0; n--) { + if (cmd->args[n].type) { + opt = &cmd->args[n]; + break; + } + } + } + return opt->type ? opt : NULL; +} + +// Set correct arg count, and fill in missing optional args. +static bool finish_cmd(struct mp_log *log, struct mp_cmd *cmd) +{ + cmd->nargs = 0; + for (int i = 0; i < MP_CMD_MAX_ARGS; i++) { + if (!cmd->args[i].type) { + const struct m_option *opt = get_arg_type(cmd->def, i); + if (!opt || is_vararg(cmd->def, i)) + break; + if (!opt->defval && !(opt->flags & MP_CMD_OPT_ARG)) { + mp_err(log, "Command %s: more than %d arguments required.\n", + cmd->name, cmd->nargs); + return false; + } + cmd->args[i].type = opt; + if (opt->defval) + m_option_copy(opt, &cmd->args[i].v, opt->defval); + } + if (cmd->args[i].type) + cmd->nargs = i + 1; + } + return true; +} + +struct mp_cmd *mp_input_parse_cmd_node(struct mp_log *log, mpv_node *node) +{ + struct mp_cmd *cmd = talloc_ptrtype(NULL, cmd); + talloc_set_destructor(cmd, destroy_cmd); + *cmd = (struct mp_cmd) { .scale = 1, }; + + if (node->format != MPV_FORMAT_NODE_ARRAY) + goto error; + mpv_node_list *args = node->u.list; + int cur = 0; + + while (cur < args->num) { + if (args->values[cur].format != MPV_FORMAT_STRING) + break; + if (!apply_flag(cmd, bstr0(args->values[cur].u.string))) + break; + cur++; + } + + bstr cmd_name = {0}; + if (cur < args->num && args->values[cur].format == MPV_FORMAT_STRING) + cmd_name = bstr0(args->values[cur++].u.string); + if (!find_cmd(log, cmd, cmd_name)) + goto error; + + int first = cur; + for (int i = 0; i < args->num - first; i++) { + const struct m_option *opt = get_arg_type(cmd->def, i); + if (!opt) { + mp_err(log, "Command %s: has only %d arguments.\n", cmd->name, i); + goto error; + } + mpv_node *val = &args->values[cur++]; + cmd->args[i].type = opt; + void *dst = &cmd->args[i].v; + if (val->format == MPV_FORMAT_STRING) { + int r = m_option_parse(log, opt, bstr0(cmd->name), + bstr0(val->u.string), dst); + if (r < 0) { + mp_err(log, "Command %s: argument %d can't be parsed: %s.\n", + cmd->name, i + 1, m_option_strerror(r)); + goto error; + } + } else { + int r = m_option_set_node(opt, dst, val); + if (r < 0) { + mp_err(log, "Command %s: argument %d has incompatible type.\n", + cmd->name, i + 1); + goto error; + } + } + } + + if (!finish_cmd(log, cmd)) + goto error; + + return cmd; +error: + for (int n = 0; n < MP_CMD_MAX_ARGS; n++) { + if (cmd->args[n].type) + m_option_free(cmd->args[n].type, &cmd->args[n].v); + } + talloc_free(cmd); + return NULL; +} + static struct mp_cmd *parse_cmd(struct parse_ctx *ctx, int def_flags) { - struct mp_cmd *cmd = NULL; - int r; + struct mp_cmd *cmd = talloc_ptrtype(NULL, cmd); + talloc_set_destructor(cmd, destroy_cmd); + *cmd = (struct mp_cmd) { .flags = def_flags, .scale = 1, }; if (!ctx->array_input) { ctx->str = bstr_lstrip(ctx->str); @@ -139,80 +283,31 @@ static struct mp_cmd *parse_cmd(struct parse_ctx *ctx, int def_flags) goto error; while (1) { - for (int n = 0; cmd_flags[n].name; n++) { - if (bstr_equals0(cur_token, cmd_flags[n].name)) { - if (pctx_read_token(ctx, &cur_token) < 0) - goto error; - def_flags &= ~cmd_flags[n].remove; - def_flags |= cmd_flags[n].add; - goto cont; - } - } - break; - cont: ; - } - - if (cur_token.len == 0) { - MP_ERR(ctx, "Command name missing.\n"); - goto error; - } - const struct mp_cmd_def *cmd_def = NULL; - for (int n = 0; mp_cmds[n].name; n++) { - if (bstr_equals0(cur_token, mp_cmds[n].name)) { - cmd_def = &mp_cmds[n]; + if (!apply_flag(cmd, cur_token)) break; - } + if (pctx_read_token(ctx, &cur_token) < 0) + goto error; + break; } - if (!cmd_def) { - MP_ERR(ctx, "Command '%.*s' not found.\n", BSTR_P(cur_token)); + if (!find_cmd(ctx->log, cmd, cur_token)) goto error; - } - - cmd = talloc_ptrtype(NULL, cmd); - talloc_set_destructor(cmd, destroy_cmd); - *cmd = (struct mp_cmd) { - .name = (char *)cmd_def->name, - .id = cmd_def->id, - .flags = def_flags, - .scale = 1, - .def = cmd_def, - }; for (int i = 0; i < MP_CMD_MAX_ARGS; i++) { - const struct m_option *opt = &cmd_def->args[i]; - bool is_vararg = cmd_def->vararg && - (i + 1 >= MP_CMD_MAX_ARGS || !cmd_def->args[i + 1].type); // last arg - if (!opt->type && is_vararg && cmd->nargs > 0) - opt = cmd->args[cmd->nargs - 1].type; - if (!opt->type) + const struct m_option *opt = get_arg_type(cmd->def, i); + if (!opt) break; - r = pctx_read_token(ctx, &cur_token); + int r = pctx_read_token(ctx, &cur_token); if (r < 0) { MP_ERR(ctx, "Command %s: error in argument %d.\n", cmd->name, i + 1); goto error; } - if (r < 1) { - if (is_vararg) - continue; - // Skip optional arguments - if (opt->defval || (opt->flags & MP_CMD_OPT_ARG)) { - struct mp_cmd_arg *cmdarg = &cmd->args[cmd->nargs]; - cmdarg->type = opt; - if (opt->defval) - m_option_copy(opt, &cmdarg->v, opt->defval); - cmd->nargs++; - continue; - } - MP_ERR(ctx, "Command %s: more than %d arguments required.\n", - cmd->name, cmd->nargs); - goto error; - } + if (r < 1) + break; - struct mp_cmd_arg *cmdarg = &cmd->args[cmd->nargs]; + struct mp_cmd_arg *cmdarg = &cmd->args[i]; cmdarg->type = opt; - cmd->nargs++; r = m_option_parse(ctx->log, opt, bstr0(cmd->name), cur_token, &cmdarg->v); if (r < 0) { MP_ERR(ctx, "Command %s: argument %d can't be parsed: %s.\n", @@ -221,6 +316,9 @@ static struct mp_cmd *parse_cmd(struct parse_ctx *ctx, int def_flags) } } + if (!finish_cmd(ctx->log, cmd)) + goto error; + bstr left = pctx_get_trailing(ctx); if (left.len) { MP_ERR(ctx, "Command %s has trailing unused arguments: '%.*s'.\n", diff --git a/input/cmd_parse.h b/input/cmd_parse.h index c8f165b11d..3c08f0b4ba 100644 --- a/input/cmd_parse.h +++ b/input/cmd_parse.h @@ -20,6 +20,7 @@ struct mp_log; struct mp_cmd; +struct mpv_node; // Parse text and return corresponding struct mp_cmd. // The location parameter is for error messages. @@ -36,6 +37,7 @@ struct mp_cmd *mp_input_parse_cmd_strv(struct mp_log *log, int def_flags, struct mp_cmd *mp_input_parse_cmd_bstrv(struct mp_log *log, int def_flags, int argc, bstr *argv, const char *location); +struct mp_cmd *mp_input_parse_cmd_node(struct mp_log *log, struct mpv_node *node); // After getting a command from mp_input_get_cmd you need to free it using this // function -- cgit v1.2.3