From 2d345c59d637ec12fbc39d97e1999c335dffcdd2 Mon Sep 17 00:00:00 2001 From: wm4 Date: Wed, 10 Jan 2018 01:20:58 +0100 Subject: input: make command argument list a dynamic array Replace the static array with dynamic memory allocation. This also requires some code to honor mp_cmd.nargs more strictly. Generally allocates more stuff. Fixes #5375 (although we could also just raise the static limit). --- input/cmd_list.c | 5 +-- input/cmd_list.h | 4 +-- input/cmd_parse.c | 93 +++++++++++++++++++++++++++---------------------------- input/input.h | 5 ++- player/command.c | 11 +++++-- 5 files changed, 63 insertions(+), 55 deletions(-) diff --git a/input/cmd_list.c b/input/cmd_list.c index 333f2cb223..dc3dd68a61 100644 --- a/input/cmd_list.c +++ b/input/cmd_list.c @@ -39,7 +39,8 @@ * (ARG_INT, ARG_FLOAT, ARG_STRING) if any, then optional arguments * (OARG_INT(default), etc) if any. The command will be given the default * argument value if the user didn't give enough arguments to specify it. - * A command can take a maximum of MP_CMD_MAX_ARGS arguments. + * A command can take a maximum of MP_CMD_DEF_MAX_ARGS arguments, or more + * if the command uses varargs. */ #define ARG_INT OPT_INT(ARG(i), 0) @@ -363,7 +364,7 @@ void mp_print_cmd_list(struct mp_log *out) for (int i = 0; mp_cmds[i].name; i++) { const struct mp_cmd_def *def = &mp_cmds[i]; mp_info(out, "%-20.20s", def->name); - for (int j = 0; j < MP_CMD_MAX_ARGS && def->args[j].type; j++) { + for (int j = 0; j < MP_CMD_DEF_MAX_ARGS && def->args[j].type; j++) { const char *type = def->args[j].type->name; if (def->args[j].defval) mp_info(out, " [%s]", type); diff --git a/input/cmd_list.h b/input/cmd_list.h index 6590158c31..4d03626bee 100644 --- a/input/cmd_list.h +++ b/input/cmd_list.h @@ -21,14 +21,14 @@ #include #include "options/m_option.h" -#define MP_CMD_MAX_ARGS 10 +#define MP_CMD_DEF_MAX_ARGS 9 #define MP_CMD_OPT_ARG 0x1000 struct mp_cmd_def { int id; // one of MP_CMD_... const char *name; // user-visible name (as used in input.conf) - const struct m_option args[MP_CMD_MAX_ARGS]; + const struct m_option args[MP_CMD_DEF_MAX_ARGS]; bool allow_auto_repeat; // react to repeated key events bool on_updown; // always emit it on both up and down key events bool vararg; // last argument can be given 0 to multiple times diff --git a/input/cmd_parse.c b/input/cmd_parse.c index 66a7cfe4b0..c4ac9dd6b7 100644 --- a/input/cmd_parse.c +++ b/input/cmd_parse.c @@ -92,46 +92,42 @@ static bool find_cmd(struct mp_log *log, struct mp_cmd *cmd, bstr name) 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); + return m->vararg && (i + 1 >= MP_CMD_DEF_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)) { + const struct m_option *opt = NULL; + if (is_vararg(cmd, i)) { // The last arg in a vararg command sets all vararg types. - for (int n = i; n >= 0; n--) { + for (int n = MPMIN(i, MP_CMD_DEF_MAX_ARGS - 1); n >= 0; n--) { if (cmd->args[n].type) { opt = &cmd->args[n]; break; } } + } else if (i < MP_CMD_DEF_MAX_ARGS) { + opt = &cmd->args[i]; } - return opt->type ? opt : NULL; + return opt && opt->type ? opt : NULL; } -// Set correct arg count, and fill in missing optional args. +// Verify that there are missing args, 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); + for (int i = cmd->nargs; i < MP_CMD_DEF_MAX_ARGS; i++) { + 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; } - if (cmd->args[i].type) - cmd->nargs = i + 1; + struct mp_cmd_arg arg = {.type = opt}; + if (opt->defval) + m_option_copy(opt, &arg.v, opt->defval); + MP_TARRAY_APPEND(cmd, cmd->args, cmd->nargs, arg); } return true; } @@ -169,8 +165,8 @@ struct mp_cmd *mp_input_parse_cmd_node(struct mp_log *log, mpv_node *node) goto error; } mpv_node *val = &args->values[cur++]; - cmd->args[i].type = opt; - void *dst = &cmd->args[i].v; + struct mp_cmd_arg arg = {.type = opt}; + void *dst = &arg.v; if (val->format == MPV_FORMAT_STRING) { int r = m_option_parse(log, opt, bstr0(cmd->name), bstr0(val->u.string), dst); @@ -187,6 +183,7 @@ struct mp_cmd *mp_input_parse_cmd_node(struct mp_log *log, mpv_node *node) goto error; } } + MP_TARRAY_APPEND(cmd, cmd->args, cmd->nargs, arg); } if (!finish_cmd(log, cmd)) @@ -194,10 +191,6 @@ struct mp_cmd *mp_input_parse_cmd_node(struct mp_log *log, mpv_node *node) 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; } @@ -293,14 +286,15 @@ static struct mp_cmd *parse_cmd_str(struct mp_log *log, void *tmp, if (r < 1) break; - struct mp_cmd_arg *cmdarg = &cmd->args[i]; - cmdarg->type = opt; - r = m_option_parse(ctx->log, opt, bstr0(cmd->name), cur_token, &cmdarg->v); + struct mp_cmd_arg arg = {.type = opt}; + r = m_option_parse(ctx->log, opt, bstr0(cmd->name), cur_token, &arg.v); if (r < 0) { MP_ERR(ctx, "Command %s: argument %d can't be parsed: %s.\n", cmd->name, i + 1, m_option_strerror(r)); goto error; } + + MP_TARRAY_APPEND(cmd, cmd->args, cmd->nargs, arg); } if (!finish_cmd(ctx->log, cmd)) @@ -360,7 +354,9 @@ mp_cmd_t *mp_input_parse_cmd_(struct mp_log *log, bstr str, const char *loc) .original = bstrdup(list, original), }; talloc_steal(list, cmd); - list->args[0].v.p = cmd; + struct mp_cmd_arg arg = {0}; + arg.v.p = cmd; + list->args = talloc_memdup(list, &arg, sizeof(arg)); p_prev = &cmd->queue_next; cmd = list; } @@ -382,19 +378,19 @@ done: struct mp_cmd *mp_input_parse_cmd_strv(struct mp_log *log, const char **argv) { - mpv_node items[MP_CMD_MAX_ARGS]; - mpv_node_list list = {.values = items}; + int count = 0; + while (argv[count]) + count++; + mpv_node *items = talloc_zero_array(NULL, mpv_node, count); + mpv_node_list list = {.values = items, .num = count}; mpv_node node = {.format = MPV_FORMAT_NODE_ARRAY, .u = {.list = &list}}; - while (argv[list.num]) { - if (list.num >= MP_CMD_MAX_ARGS) { - mp_err(log, "Too many arguments to command.\n"); - return NULL; - } - char *s = (char *)argv[list.num]; - items[list.num++] = (mpv_node){.format = MPV_FORMAT_STRING, - .u = {.string = s}}; + for (int n = 0; n < count; n++) { + items[n] = (mpv_node){.format = MPV_FORMAT_STRING, + .u = {.string = (char *)argv[n]}}; } - return mp_input_parse_cmd_node(log, &node); + struct mp_cmd *res = mp_input_parse_cmd_node(log, &node); + talloc_free(items); + return res; } void mp_cmd_free(mp_cmd_t *cmd) @@ -413,8 +409,9 @@ mp_cmd_t *mp_cmd_clone(mp_cmd_t *cmd) ret = talloc_memdup(NULL, cmd, sizeof(mp_cmd_t)); talloc_set_destructor(ret, destroy_cmd); ret->name = talloc_strdup(ret, cmd->name); + ret->args = talloc_zero_array(ret, struct mp_cmd_arg, ret->nargs); for (i = 0; i < ret->nargs; i++) { - memset(&ret->args[i].v, 0, ret->args[i].type->type->size); + ret->args[i].type = cmd->args[i].type; m_option_copy(ret->args[i].type, &ret->args[i].v, &cmd->args[i].v); } ret->original = bstrdup(ret, cmd->original); @@ -428,7 +425,9 @@ mp_cmd_t *mp_cmd_clone(mp_cmd_t *cmd) if (prev) { prev->queue_next = sub; } else { - ret->args[0].v.p = sub; + struct mp_cmd_arg arg = {0}; + arg.v.p = sub; + ret->args = talloc_memdup(ret, &arg, sizeof(arg)); } prev = sub; } diff --git a/input/input.h b/input/input.h index f00eb9b0e2..16b203a5e7 100644 --- a/input/input.h +++ b/input/input.h @@ -56,6 +56,9 @@ enum mp_input_section_flags { struct input_ctx; struct mp_log; +// Arbitrary upper bound for sanity. +#define MP_CMD_MAX_ARGS 100 + struct mp_cmd_arg { const struct m_option *type; union { @@ -71,7 +74,7 @@ struct mp_cmd_arg { typedef struct mp_cmd { int id; char *name; - struct mp_cmd_arg args[MP_CMD_MAX_ARGS]; + struct mp_cmd_arg *args; int nargs; int flags; // mp_cmd_flags bitfield bstr original; diff --git a/player/command.c b/player/command.c index 006f557eb8..2496b396ff 100644 --- a/player/command.c +++ b/player/command.c @@ -4991,7 +4991,7 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re } case MP_CMD_CYCLE_VALUES: { - char *args[MP_CMD_MAX_ARGS + 1] = {0}; + char **args = talloc_zero_array(NULL, char *, cmd->nargs + 1); for (int n = 0; n < cmd->nargs; n++) args[n] = cmd->args[n].v.s; int first = 1, dir = 1; @@ -5015,14 +5015,17 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re } else if (r == M_PROPERTY_UNKNOWN) { set_osd_msg(mpctx, osdl, osd_duration, "Unknown property: '%s'", property); + talloc_free(args); return -1; } else if (r <= 0) { set_osd_msg(mpctx, osdl, osd_duration, "Failed to set property '%s' to '%s'", property, value); + talloc_free(args); return -1; } } + talloc_free(args); break; } @@ -5390,10 +5393,11 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re } case MP_CMD_RUN: { - char *args[MP_CMD_MAX_ARGS + 1] = {0}; + char **args = talloc_zero_array(NULL, char *, cmd->nargs + 1); for (int n = 0; n < cmd->nargs; n++) args[n] = cmd->args[n].v.s; mp_subprocess_detached(mpctx->log, args); + talloc_free(args); break; } @@ -5510,11 +5514,12 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re break; } case MP_CMD_SCRIPT_MESSAGE: { - const char *args[MP_CMD_MAX_ARGS]; + const char **args = talloc_array(NULL, const char *, cmd->nargs); mpv_event_client_message event = {.args = args}; for (int n = 0; n < cmd->nargs; n++) event.args[event.num_args++] = cmd->args[n].v.s; mp_client_broadcast_event(mpctx, MPV_EVENT_CLIENT_MESSAGE, &event); + talloc_free(args); break; } -- cgit v1.2.3