mirror of https://github.com/mpv-player/mpv
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:
parent
e3bee23fe4
commit
2d345c59d6
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue