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
|
* (ARG_INT, ARG_FLOAT, ARG_STRING) if any, then optional arguments
|
||||||
* (OARG_INT(default), etc) if any. The command will be given the default
|
* (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.
|
* 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)
|
#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++) {
|
for (int i = 0; mp_cmds[i].name; i++) {
|
||||||
const struct mp_cmd_def *def = &mp_cmds[i];
|
const struct mp_cmd_def *def = &mp_cmds[i];
|
||||||
mp_info(out, "%-20.20s", def->name);
|
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;
|
const char *type = def->args[j].type->name;
|
||||||
if (def->args[j].defval)
|
if (def->args[j].defval)
|
||||||
mp_info(out, " [%s]", type);
|
mp_info(out, " [%s]", type);
|
||||||
|
|
|
@ -21,14 +21,14 @@
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include "options/m_option.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
|
#define MP_CMD_OPT_ARG 0x1000
|
||||||
|
|
||||||
struct mp_cmd_def {
|
struct mp_cmd_def {
|
||||||
int id; // one of MP_CMD_...
|
int id; // one of MP_CMD_...
|
||||||
const char *name; // user-visible name (as used in input.conf)
|
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 allow_auto_repeat; // react to repeated key events
|
||||||
bool on_updown; // always emit it on both up and down 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
|
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)
|
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)
|
static const struct m_option *get_arg_type(const struct mp_cmd_def *cmd, int i)
|
||||||
{
|
{
|
||||||
if (i >= MP_CMD_MAX_ARGS)
|
const struct m_option *opt = NULL;
|
||||||
return NULL;
|
if (is_vararg(cmd, i)) {
|
||||||
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.
|
// 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) {
|
if (cmd->args[n].type) {
|
||||||
opt = &cmd->args[n];
|
opt = &cmd->args[n];
|
||||||
break;
|
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)
|
static bool finish_cmd(struct mp_log *log, struct mp_cmd *cmd)
|
||||||
{
|
{
|
||||||
cmd->nargs = 0;
|
for (int i = cmd->nargs; i < MP_CMD_DEF_MAX_ARGS; i++) {
|
||||||
for (int i = 0; i < MP_CMD_MAX_ARGS; i++) {
|
const struct m_option *opt = get_arg_type(cmd->def, i);
|
||||||
if (!cmd->args[i].type) {
|
if (!opt || is_vararg(cmd->def, i))
|
||||||
const struct m_option *opt = get_arg_type(cmd->def, i);
|
break;
|
||||||
if (!opt || is_vararg(cmd->def, i))
|
if (!opt->defval && !(opt->flags & MP_CMD_OPT_ARG)) {
|
||||||
break;
|
mp_err(log, "Command %s: more than %d arguments required.\n",
|
||||||
if (!opt->defval && !(opt->flags & MP_CMD_OPT_ARG)) {
|
cmd->name, cmd->nargs);
|
||||||
mp_err(log, "Command %s: more than %d arguments required.\n",
|
return false;
|
||||||
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)
|
struct mp_cmd_arg arg = {.type = opt};
|
||||||
cmd->nargs = i + 1;
|
if (opt->defval)
|
||||||
|
m_option_copy(opt, &arg.v, opt->defval);
|
||||||
|
MP_TARRAY_APPEND(cmd, cmd->args, cmd->nargs, arg);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -169,8 +165,8 @@ struct mp_cmd *mp_input_parse_cmd_node(struct mp_log *log, mpv_node *node)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
mpv_node *val = &args->values[cur++];
|
mpv_node *val = &args->values[cur++];
|
||||||
cmd->args[i].type = opt;
|
struct mp_cmd_arg arg = {.type = opt};
|
||||||
void *dst = &cmd->args[i].v;
|
void *dst = &arg.v;
|
||||||
if (val->format == MPV_FORMAT_STRING) {
|
if (val->format == MPV_FORMAT_STRING) {
|
||||||
int r = m_option_parse(log, opt, bstr0(cmd->name),
|
int r = m_option_parse(log, opt, bstr0(cmd->name),
|
||||||
bstr0(val->u.string), dst);
|
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;
|
goto error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
MP_TARRAY_APPEND(cmd, cmd->args, cmd->nargs, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!finish_cmd(log, cmd))
|
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;
|
return cmd;
|
||||||
error:
|
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);
|
talloc_free(cmd);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -293,14 +286,15 @@ static struct mp_cmd *parse_cmd_str(struct mp_log *log, void *tmp,
|
||||||
if (r < 1)
|
if (r < 1)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
struct mp_cmd_arg *cmdarg = &cmd->args[i];
|
struct mp_cmd_arg arg = {.type = opt};
|
||||||
cmdarg->type = opt;
|
r = m_option_parse(ctx->log, opt, bstr0(cmd->name), cur_token, &arg.v);
|
||||||
r = m_option_parse(ctx->log, opt, bstr0(cmd->name), cur_token, &cmdarg->v);
|
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
MP_ERR(ctx, "Command %s: argument %d can't be parsed: %s.\n",
|
MP_ERR(ctx, "Command %s: argument %d can't be parsed: %s.\n",
|
||||||
cmd->name, i + 1, m_option_strerror(r));
|
cmd->name, i + 1, m_option_strerror(r));
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MP_TARRAY_APPEND(cmd, cmd->args, cmd->nargs, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!finish_cmd(ctx->log, cmd))
|
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),
|
.original = bstrdup(list, original),
|
||||||
};
|
};
|
||||||
talloc_steal(list, cmd);
|
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;
|
p_prev = &cmd->queue_next;
|
||||||
cmd = list;
|
cmd = list;
|
||||||
}
|
}
|
||||||
|
@ -382,19 +378,19 @@ done:
|
||||||
|
|
||||||
struct mp_cmd *mp_input_parse_cmd_strv(struct mp_log *log, const char **argv)
|
struct mp_cmd *mp_input_parse_cmd_strv(struct mp_log *log, const char **argv)
|
||||||
{
|
{
|
||||||
mpv_node items[MP_CMD_MAX_ARGS];
|
int count = 0;
|
||||||
mpv_node_list list = {.values = items};
|
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}};
|
mpv_node node = {.format = MPV_FORMAT_NODE_ARRAY, .u = {.list = &list}};
|
||||||
while (argv[list.num]) {
|
for (int n = 0; n < count; n++) {
|
||||||
if (list.num >= MP_CMD_MAX_ARGS) {
|
items[n] = (mpv_node){.format = MPV_FORMAT_STRING,
|
||||||
mp_err(log, "Too many arguments to command.\n");
|
.u = {.string = (char *)argv[n]}};
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
char *s = (char *)argv[list.num];
|
|
||||||
items[list.num++] = (mpv_node){.format = MPV_FORMAT_STRING,
|
|
||||||
.u = {.string = s}};
|
|
||||||
}
|
}
|
||||||
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)
|
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));
|
ret = talloc_memdup(NULL, cmd, sizeof(mp_cmd_t));
|
||||||
talloc_set_destructor(ret, destroy_cmd);
|
talloc_set_destructor(ret, destroy_cmd);
|
||||||
ret->name = talloc_strdup(ret, cmd->name);
|
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++) {
|
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);
|
m_option_copy(ret->args[i].type, &ret->args[i].v, &cmd->args[i].v);
|
||||||
}
|
}
|
||||||
ret->original = bstrdup(ret, cmd->original);
|
ret->original = bstrdup(ret, cmd->original);
|
||||||
|
@ -428,7 +425,9 @@ mp_cmd_t *mp_cmd_clone(mp_cmd_t *cmd)
|
||||||
if (prev) {
|
if (prev) {
|
||||||
prev->queue_next = sub;
|
prev->queue_next = sub;
|
||||||
} else {
|
} 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;
|
prev = sub;
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,9 @@ enum mp_input_section_flags {
|
||||||
struct input_ctx;
|
struct input_ctx;
|
||||||
struct mp_log;
|
struct mp_log;
|
||||||
|
|
||||||
|
// Arbitrary upper bound for sanity.
|
||||||
|
#define MP_CMD_MAX_ARGS 100
|
||||||
|
|
||||||
struct mp_cmd_arg {
|
struct mp_cmd_arg {
|
||||||
const struct m_option *type;
|
const struct m_option *type;
|
||||||
union {
|
union {
|
||||||
|
@ -71,7 +74,7 @@ struct mp_cmd_arg {
|
||||||
typedef struct mp_cmd {
|
typedef struct mp_cmd {
|
||||||
int id;
|
int id;
|
||||||
char *name;
|
char *name;
|
||||||
struct mp_cmd_arg args[MP_CMD_MAX_ARGS];
|
struct mp_cmd_arg *args;
|
||||||
int nargs;
|
int nargs;
|
||||||
int flags; // mp_cmd_flags bitfield
|
int flags; // mp_cmd_flags bitfield
|
||||||
bstr original;
|
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: {
|
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++)
|
for (int n = 0; n < cmd->nargs; n++)
|
||||||
args[n] = cmd->args[n].v.s;
|
args[n] = cmd->args[n].v.s;
|
||||||
int first = 1, dir = 1;
|
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) {
|
} else if (r == M_PROPERTY_UNKNOWN) {
|
||||||
set_osd_msg(mpctx, osdl, osd_duration,
|
set_osd_msg(mpctx, osdl, osd_duration,
|
||||||
"Unknown property: '%s'", property);
|
"Unknown property: '%s'", property);
|
||||||
|
talloc_free(args);
|
||||||
return -1;
|
return -1;
|
||||||
} else if (r <= 0) {
|
} else if (r <= 0) {
|
||||||
set_osd_msg(mpctx, osdl, osd_duration,
|
set_osd_msg(mpctx, osdl, osd_duration,
|
||||||
"Failed to set property '%s' to '%s'",
|
"Failed to set property '%s' to '%s'",
|
||||||
property, value);
|
property, value);
|
||||||
|
talloc_free(args);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
talloc_free(args);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5390,10 +5393,11 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
|
||||||
}
|
}
|
||||||
|
|
||||||
case MP_CMD_RUN: {
|
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++)
|
for (int n = 0; n < cmd->nargs; n++)
|
||||||
args[n] = cmd->args[n].v.s;
|
args[n] = cmd->args[n].v.s;
|
||||||
mp_subprocess_detached(mpctx->log, args);
|
mp_subprocess_detached(mpctx->log, args);
|
||||||
|
talloc_free(args);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5510,11 +5514,12 @@ int run_command(struct MPContext *mpctx, struct mp_cmd *cmd, struct mpv_node *re
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MP_CMD_SCRIPT_MESSAGE: {
|
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};
|
mpv_event_client_message event = {.args = args};
|
||||||
for (int n = 0; n < cmd->nargs; n++)
|
for (int n = 0; n < cmd->nargs; n++)
|
||||||
event.args[event.num_args++] = cmd->args[n].v.s;
|
event.args[event.num_args++] = cmd->args[n].v.s;
|
||||||
mp_client_broadcast_event(mpctx, MPV_EVENT_CLIENT_MESSAGE, &event);
|
mp_client_broadcast_event(mpctx, MPV_EVENT_CLIENT_MESSAGE, &event);
|
||||||
|
talloc_free(args);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue