| @@ -18,6 +18,7 @@ · 17 unmodified lines |
| 18 | #include <stddef.h> | 18 | #include <stddef.h> |
| 19 | | 19 | |
| 20 | #include "misc/bstr.h" | 20 | #include "misc/bstr.h" |
| | 21 | #include "misc/node.h" |
| 21 | #include "common/common.h" | 22 | #include "common/common.h" |
| 22 | #include "common/msg.h" | 23 | #include "common/msg.h" |
| 23 | #include "options/m_option.h" | 24 | #include "options/m_option.h" |
| @@ -30,8 +31,10 @@ · 6 unmodified lines |
| 30 | static void destroy_cmd(void *ptr) | 31 | static void destroy_cmd(void *ptr) |
| 31 | { | 32 | { |
| 32 | struct mp_cmd *cmd = ptr; | 33 | struct mp_cmd *cmd = ptr; |
| 33 | for (int n = 0; n < cmd->nargs; n++) | 34 | for (int n = 0; n < cmd->nargs; n++) { |
| 34 | m_option_free(cmd->args[n].type, &cmd->args[n].v); | 35 | if (cmd->args[n].type) |
| | 36 | m_option_free(cmd->args[n].type, &cmd->args[n].v); |
| | 37 | } |
| 35 | } | 38 | } |
| 36 | | 39 | |
| 37 | struct flag { | 40 | struct flag { |
| @@ -111,22 +114,37 @@ static const struct m_option *get_arg_type(const struct mp_cmd_def *cmd, int i) · 73 unmodified lines |
| 111 | return opt && opt->type ? opt : NULL; | 114 | return opt && opt->type ? opt : NULL; |
| 112 | } | 115 | } |
| 113 | | 116 | |
| 114 | // Verify that there are missing args, fill in missing optional args. | 117 | // Return the name of the argument, possibly as stack allocated string (which is |
| | 118 | // why this is a macro, and out of laziness). Otherwise as get_arg_type(). |
| | 119 | #define get_arg_name(cmd, i) \ |
| | 120 | ((i) < MP_CMD_DEF_MAX_ARGS && (cmd)->args[(i)].name && \ |
| | 121 | (cmd)->args[(i)].name[0] \ |
| | 122 | ? (cmd)->args[(i)].name : mp_tprintf(10, "%d", (i) + 1)) |
| | 123 | |
| | 124 | // Verify that there are no missing args, fill in missing optional args. |
| 115 | static bool finish_cmd(struct mp_log *log, struct mp_cmd *cmd) | 125 | static bool finish_cmd(struct mp_log *log, struct mp_cmd *cmd) |
| 116 | { | 126 | { |
| 117 | for (int i = cmd->nargs; i < MP_CMD_DEF_MAX_ARGS; i++) { | 127 | for (int i = 0; i < MP_CMD_DEF_MAX_ARGS; i++) { |
| | 128 | // (type==NULL is used for yet unset arguments) |
| | 129 | if (i < cmd->nargs && cmd->args[i].type) |
| | 130 | continue; |
| 118 | const struct m_option *opt = get_arg_type(cmd->def, i); | 131 | const struct m_option *opt = get_arg_type(cmd->def, i); |
| 119 | if (!opt || is_vararg(cmd->def, i)) | 132 | if (i >= cmd->nargs && (!opt || is_vararg(cmd->def, i))) |
| 120 | break; | 133 | break; |
| 121 | if (!opt->defval && !(opt->flags & MP_CMD_OPT_ARG)) { | 134 | if (!opt->defval && !(opt->flags & MP_CMD_OPT_ARG)) { |
| 122 | mp_err(log, "Command %s: more than %d arguments required.\n", | 135 | mp_err(log, "Command %s: required argument %s not set.\n", |
| 123 | cmd->name, cmd->nargs); | 136 | cmd->name, get_arg_name(cmd->def, i)); |
| 124 | return false; | 137 | return false; |
| 125 | } | 138 | } |
| 126 | struct mp_cmd_arg arg = {.type = opt}; | 139 | struct mp_cmd_arg arg = {.type = opt}; |
| 127 | if (opt->defval) | 140 | if (opt->defval) |
| 128 | m_option_copy(opt, &arg.v, opt->defval); | 141 | m_option_copy(opt, &arg.v, opt->defval); |
| 129 | MP_TARRAY_APPEND(cmd, cmd->args, cmd->nargs, arg); | 142 | assert(i <= cmd->nargs); |
| | 143 | if (i == cmd->nargs) { |
| | 144 | MP_TARRAY_APPEND(cmd, cmd->args, cmd->nargs, arg); |
| | 145 | } else { |
| | 146 | cmd->args[i] = arg; |
| | 147 | } |
| 130 | } | 148 | } |
| 131 | | 149 | |
| 132 | if (!(cmd->flags & (MP_ASYNC_CMD | MP_SYNC_CMD))) | 150 | if (!(cmd->flags & (MP_ASYNC_CMD | MP_SYNC_CMD))) |
| @@ -135,14 +153,54 @@ static bool finish_cmd(struct mp_log *log, struct mp_cmd *cmd) · 2 unmodified lines |
| 135 | return true; | 153 | return true; |
| 136 | } | 154 | } |
| 137 | | 155 | |
| 138 | struct mp_cmd *mp_input_parse_cmd_node(struct mp_log *log, mpv_node *node) | 156 | static bool set_node_arg(struct mp_log *log, struct mp_cmd *cmd, int i, |
| | 157 | mpv_node *val) |
| 139 | { | 158 | { |
| 140 | struct mp_cmd *cmd = talloc_ptrtype(NULL, cmd); | 159 | const char *name = get_arg_name(cmd->def, i); |
| 141 | talloc_set_destructor(cmd, destroy_cmd); | | |
| 142 | *cmd = (struct mp_cmd) { .scale = 1, .scale_units = 1 }; | | |
| 143 | | 160 | |
| 144 | if (node->format != MPV_FORMAT_NODE_ARRAY) | 161 | const struct m_option *opt = get_arg_type(cmd->def, i); |
| 145 | goto error; | 162 | if (!opt) { |
| | 163 | mp_err(log, "Command %s: has only %d arguments.\n", cmd->name, i); |
| | 164 | return false; |
| | 165 | } |
| | 166 | |
| | 167 | if (i < cmd->nargs && cmd->args[i].type) { |
| | 168 | mp_err(log, "Command %s: argument %s was already set.\n", cmd->name, name); |
| | 169 | return false; |
| | 170 | } |
| | 171 | |
| | 172 | struct mp_cmd_arg arg = {.type = opt}; |
| | 173 | void *dst = &arg.v; |
| | 174 | if (val->format == MPV_FORMAT_STRING) { |
| | 175 | int r = m_option_parse(log, opt, bstr0(cmd->name), |
| | 176 | bstr0(val->u.string), dst); |
| | 177 | if (r < 0) { |
| | 178 | mp_err(log, "Command %s: argument %s can't be parsed: %s.\n", |
| | 179 | cmd->name, name, m_option_strerror(r)); |
| | 180 | return false; |
| | 181 | } |
| | 182 | } else { |
| | 183 | int r = m_option_set_node(opt, dst, val); |
| | 184 | if (r < 0) { |
| | 185 | mp_err(log, "Command %s: argument %s has incompatible type.\n", |
| | 186 | cmd->name, name); |
| | 187 | return false; |
| | 188 | } |
| | 189 | } |
| | 190 | |
| | 191 | // (leave unset arguments blank, to be set later or checked by finish_cmd()) |
| | 192 | while (i >= cmd->nargs) { |
| | 193 | struct mp_cmd_arg t = {0}; |
| | 194 | MP_TARRAY_APPEND(cmd, cmd->args, cmd->nargs, t); |
| | 195 | } |
| | 196 | |
| | 197 | cmd->args[i] = arg; |
| | 198 | return true; |
| | 199 | } |
| | 200 | |
| | 201 | static bool cmd_node_array(struct mp_log *log, struct mp_cmd *cmd, mpv_node *node) |
| | 202 | { |
| | 203 | assert(node->format == MPV_FORMAT_NODE_ARRAY); |
| 146 | mpv_node_list *args = node->u.list; | 204 | mpv_node_list *args = node->u.list; |
| 147 | int cur = 0; | 205 | int cur = 0; |
| 148 | | 206 | |
| @@ -158,47 +216,90 @@ struct mp_cmd *mp_input_parse_cmd_node(struct mp_log *log, mpv_node *node) · 9 unmodified lines |
| 158 | if (cur < args->num && args->values[cur].format == MPV_FORMAT_STRING) | 216 | if (cur < args->num && args->values[cur].format == MPV_FORMAT_STRING) |
| 159 | cmd_name = bstr0(args->values[cur++].u.string); | 217 | cmd_name = bstr0(args->values[cur++].u.string); |
| 160 | if (!find_cmd(log, cmd, cmd_name)) | 218 | if (!find_cmd(log, cmd, cmd_name)) |
| 161 | goto error; | 219 | return false; |
| 162 | | 220 | |
| 163 | int first = cur; | 221 | int first = cur; |
| 164 | for (int i = 0; i < args->num - first; i++) { | 222 | for (int i = 0; i < args->num - first; i++) { |
| 165 | const struct m_option *opt = get_arg_type(cmd->def, i); | 223 | if (!set_node_arg(log, cmd, cmd->nargs, &args->values[cur++])) |
| 166 | if (!opt) { | 224 | return false; |
| 167 | mp_err(log, "Command %s: has only %d arguments.\n", cmd->name, i); | 225 | } |
| 168 | goto error; | 226 | |
| 169 | } | 227 | return true; |
| 170 | mpv_node *val = &args->values[cur++]; | 228 | } |
| 171 | struct mp_cmd_arg arg = {.type = opt}; | 229 | |
| 172 | void *dst = &arg.v; | 230 | static bool cmd_node_map(struct mp_log *log, struct mp_cmd *cmd, mpv_node *node) |
| 173 | if (val->format == MPV_FORMAT_STRING) { | 231 | { |
| 174 | int r = m_option_parse(log, opt, bstr0(cmd->name), | 232 | assert(node->format == MPV_FORMAT_NODE_MAP); |
| 175 | bstr0(val->u.string), dst); | 233 | mpv_node_list *args = node->u.list; |
| 176 | if (r < 0) { | 234 | |
| 177 | mp_err(log, "Command %s: argument %d can't be parsed: %s.\n", | 235 | mpv_node *name = node_map_get(node, "name"); |
| 178 | cmd->name, i + 1, m_option_strerror(r)); | 236 | if (!name || name->format != MPV_FORMAT_STRING) |
| 179 | goto error; | 237 | return false; |
| | 238 | |
| | 239 | if (!find_cmd(log, cmd, bstr0(name->u.string))) |
| | 240 | return false; |
| | 241 | |
| | 242 | for (int n = 0; n < args->num; n++) { |
| | 243 | const char *key = args->keys[n]; |
| | 244 | mpv_node *val = &args->values[n]; |
| | 245 | |
| | 246 | if (strcmp(key, "name") == 0) { |
| | 247 | // already handled above |
| | 248 | } else if (strcmp(key, "_flags") == 0) { |
| | 249 | if (val->format != MPV_FORMAT_NODE_ARRAY) |
| | 250 | return false; |
| | 251 | mpv_node_list *flags = val->u.list; |
| | 252 | for (int i = 0; i < flags->num; i++) { |
| | 253 | if (flags->values[i].format != MPV_FORMAT_STRING) |
| | 254 | return false; |
| | 255 | if (!apply_flag(cmd, bstr0(flags->values[i].u.string))) |
| | 256 | return false; |
| 180 | } | 257 | } |
| 181 | } else { | 258 | } else { |
| 182 | int r = m_option_set_node(opt, dst, val); | 259 | int arg = -1; |
| 183 | if (r < 0) { | 260 | |
| 184 | mp_err(log, "Command %s: argument %d has incompatible type.\n", | 261 | for (int i = 0; i < MP_CMD_DEF_MAX_ARGS; i++) { |
| 185 | cmd->name, i + 1); | 262 | const char *arg_name = cmd->def->args[i].name; |
| 186 | goto error; | 263 | if (arg_name && arg_name[0] && strcmp(key, arg_name) == 0) { |
| | 264 | arg = i; |
| | 265 | break; |
| | 266 | } |
| | 267 | } |
| | 268 | |
| | 269 | if (arg < 0) { |
| | 270 | mp_err(log, "Command %s: no argument %s.\n", cmd->name, key); |
| | 271 | return false; |
| 187 | } | 272 | } |
| | 273 | |
| | 274 | if (!set_node_arg(log, cmd, arg, val)) |
| | 275 | return false; |
| 188 | } | 276 | } |
| 189 | MP_TARRAY_APPEND(cmd, cmd->args, cmd->nargs, arg); | | |
| 190 | } | 277 | } |
| 191 | | 278 | |
| 192 | if (!finish_cmd(log, cmd)) | 279 | return true; |
| 193 | goto error; | 280 | } |
| | 281 | |
| | 282 | struct mp_cmd *mp_input_parse_cmd_node(struct mp_log *log, mpv_node *node) |
| | 283 | { |
| | 284 | struct mp_cmd *cmd = talloc_ptrtype(NULL, cmd); |
| | 285 | talloc_set_destructor(cmd, destroy_cmd); |
| | 286 | *cmd = (struct mp_cmd) { .scale = 1, .scale_units = 1 }; |
| | 287 | |
| | 288 | bool res = false; |
| | 289 | if (node->format == MPV_FORMAT_NODE_ARRAY) { |
| | 290 | res = cmd_node_array(log, cmd, node); |
| | 291 | } else if (node->format == MPV_FORMAT_NODE_MAP) { |
| | 292 | res = cmd_node_map(log, cmd, node); |
| | 293 | } |
| | 294 | |
| | 295 | res = res && finish_cmd(log, cmd); |
| | 296 | |
| | 297 | if (!res) |
| | 298 | TA_FREEP(&cmd); |
| 194 | | 299 | |
| 195 | return cmd; | 300 | return cmd; |
| 196 | error: | | |
| 197 | talloc_free(cmd); | | |
| 198 | return NULL; | | |
| 199 | } | 301 | } |
| 200 | | 302 | |
| 201 | | | |
| 202 | static bool read_token(bstr str, bstr *out_rest, bstr *out_token) | 303 | static bool read_token(bstr str, bstr *out_rest, bstr *out_token) |
| 203 | { | 304 | { |
| 204 | bstr t = bstr_lstrip(str); | 305 | bstr t = bstr_lstrip(str); |