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).
This commit is contained in:
wm4 2018-01-10 01:20:58 +01:00 committed by Kevin Mitchell
parent e3bee23fe4
commit 2d345c59d6
5 changed files with 63 additions and 55 deletions

View File

@ -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);

View File

@ -21,14 +21,14 @@
#include <stdbool.h>
#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

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
}