summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bstr.h2
-rw-r--r--etc/input.conf8
-rw-r--r--input/input.c282
-rw-r--r--m_property.c30
4 files changed, 163 insertions, 159 deletions
diff --git a/bstr.h b/bstr.h
index 5f04a75db8..f6ce407a80 100644
--- a/bstr.h
+++ b/bstr.h
@@ -152,7 +152,7 @@ static inline int bstr_find0(struct bstr haystack, const char *needle)
return bstr_find(haystack, bstr0(needle));
}
-static inline int bstr_eatstart0(struct bstr *s, char *prefix)
+static inline int bstr_eatstart0(struct bstr *s, const char *prefix)
{
return bstr_eatstart(s, bstr0(prefix));
}
diff --git a/etc/input.conf b/etc/input.conf
index a32495ab3a..257a4538c9 100644
--- a/etc/input.conf
+++ b/etc/input.conf
@@ -11,15 +11,13 @@
#
# Note that merely removing default key bindings from this file won't remove
# the default bindings mplayer was compiled with, unless
-# --input=nodefault-bindings
+# --input=no-default-bindings
# is specified.
#
# Lines starting with # are comments. Use SHARP to assign the # key.
#
-# Some characters need to be escaped. In particular, if you want to display
-# a '\' character as part of an osd_show_property_text OSD message, you have to
-# escape 2 times:
-# key osd_show_property_text "This is a single backslash: \\\\!"
+# Strings need to be quoted and escaped:
+# KEY show_text "This is a single backslash: \\ and a quote: \" !"
#
# You can use modifier-key combinations like Shift+Left or Ctrl+Alt+x with
# modifiers Shift, Ctrl, Alt and Meta, but note that currently reading
diff --git a/input/input.c b/input/input.c
index 5a0a8fd49c..00544e38d8 100644
--- a/input/input.c
+++ b/input/input.c
@@ -691,185 +691,217 @@ int mp_input_add_key_fd(struct input_ctx *ictx, int fd, int select,
return 1;
}
-static char *skip_ws(char *str)
+static bool read_token(bstr str, bstr *out_rest, bstr *out_token)
{
- while (str[0] == ' ' || str[0] == '\t')
- ++str;
- return str;
+ bstr t = bstr_lstrip(str);
+ int next = bstrcspn(t, WHITESPACE "#");
+ // Handle comments
+ if (t.start[next] == '#')
+ t = bstr_splice(t, 0, next);
+ if (!t.len)
+ return false;
+ *out_token = bstr_splice(t, 0, next);
+ *out_rest = bstr_cut(t, next);
+ return true;
}
-static char *skip_no_ws(char *str)
+static bool eat_token(bstr *str, const char *tok)
{
- while (str[0] && !(str[0] == ' ' || str[0] == '\t'))
- ++str;
- return str;
+ bstr rest, token;
+ if (read_token(*str, &rest, &token) && bstrcmp0(token, tok) == 0) {
+ *str = rest;
+ return true;
+ }
+ return false;
}
-mp_cmd_t *mp_input_parse_cmd(bstr str_b)
+static bool append_escape(bstr *code, char **str)
+{
+ if (code->len < 1)
+ return false;
+ char replace = 0;
+ switch (code->start[0]) {
+ case '"': replace = '"'; break;
+ case '\\': replace = '\\'; break;
+ case 'b': replace = '\b'; break;
+ case 'f': replace = '\f'; break;
+ case 'n': replace = '\n'; break;
+ case 'r': replace = '\r'; break;
+ case 't': replace = '\t'; break;
+ case 'e': replace = '\x1b'; break;
+ case '\'': replace = '\''; break;
+ }
+ if (replace) {
+ *str = talloc_strndup_append_buffer(*str, &replace, 1);
+ *code = bstr_cut(*code, 1);
+ return true;
+ }
+ if (code->start[0] == 'x' && code->len >= 3) {
+ bstr num = bstr_splice(*code, 1, 3);
+ char c = bstrtoll(num, &num, 16);
+ if (!num.len)
+ return false;
+ *str = talloc_strndup_append_buffer(*str, &c, 1);
+ *code = bstr_cut(*code, 3);
+ return true;
+ }
+ if (code->start[0] == 'u' && code->len >= 5) {
+ bstr num = bstr_splice(*code, 1, 5);
+ int c = bstrtoll(num, &num, 16);
+ if (num.len)
+ return false;
+ *str = append_utf8_buffer(*str, c);
+ *code = bstr_cut(*code, 5);
+ return true;
+ }
+ return false;
+}
+
+static bool read_escaped_string(void *talloc_ctx, bstr *str, bstr *literal)
+{
+ bstr t = *str;
+ char *new = talloc_strdup(talloc_ctx, "");
+ while (t.len) {
+ if (t.start[0] == '"')
+ break;
+ if (t.start[0] == '\\') {
+ t = bstr_cut(t, 1);
+ if (!append_escape(&t, &new))
+ goto error;
+ } else {
+ new = talloc_strndup_append_buffer(new, t.start, 1);
+ t = bstr_cut(t, 1);
+ }
+ }
+ int len = str->len - t.len;
+ *literal = new ? bstr0(new) : bstr_splice(*str, 0, len);
+ *str = bstr_cut(*str, len);
+ return true;
+error:
+ talloc_free(new);
+ return false;
+}
+
+mp_cmd_t *mp_input_parse_cmd(bstr str)
{
- int i, l;
int pausing = 0;
int on_osd = MP_ON_OSD_AUTO;
- char *ptr;
- const mp_cmd_t *cmd_def;
- mp_cmd_t *cmd = NULL;
+ struct mp_cmd *cmd = NULL;
void *tmp = talloc_new(NULL);
- char *str = bstrdup0(tmp, str_b);
-
- str = skip_ws(str);
- if (strncmp(str, "pausing ", 8) == 0) {
+ if (eat_token(&str, "pausing")) {
pausing = 1;
- str = &str[8];
- } else if (strncmp(str, "pausing_keep ", 13) == 0) {
+ } else if (eat_token(&str, "pausing_keep")) {
pausing = 2;
- str = &str[13];
- } else if (strncmp(str, "pausing_toggle ", 15) == 0) {
+ } else if (eat_token(&str, "pausing_toggle")) {
pausing = 3;
- str = &str[15];
- } else if (strncmp(str, "pausing_keep_force ", 19) == 0) {
+ } else if (eat_token(&str, "pausing_keep_force")) {
pausing = 4;
- str = &str[19];
}
- str = skip_ws(str);
-
+ str = bstr_lstrip(str);
for (const struct legacy_cmd *entry = legacy_cmds; entry->old; entry++) {
size_t old_len = strlen(entry->old);
- if (strncasecmp(entry->old, str, old_len) == 0) {
+ if (bstrcasecmp(bstr_splice(str, 0, old_len),
+ (bstr) {(char *)entry->old, old_len}) == 0)
+ {
mp_tmsg(MSGT_INPUT, MSGL_WARN, "Warning: command '%s' is "
"deprecated, replaced with '%s'. Fix your input.conf!\n",
entry->old, entry->new);
- str = talloc_asprintf(tmp, "%s%s", entry->new, str + old_len);
+ bstr s = bstr_cut(str, old_len);
+ str = bstr0(talloc_asprintf(tmp, "%s%.*s", entry->new, BSTR_P(s)));
break;
}
}
- str = skip_ws(str);
-
- if (strncmp(str, "no-osd ", 7) == 0) {
+ if (eat_token(&str, "no-osd")) {
on_osd = MP_ON_OSD_NO;
- str = &str[7];
}
- ptr = skip_no_ws(str);
- if (*ptr != 0)
- l = ptr - str;
- else
- l = strlen(str);
-
- if (l == 0)
- goto error;
-
- for (i = 0; mp_cmds[i].name != NULL; i++) {
- const char *cmd = mp_cmds[i].name;
- if (strncasecmp(cmd, str, l) == 0 && strlen(cmd) == l)
+ int cmd_idx = 0;
+ while (mp_cmds[cmd_idx].name != NULL) {
+ if (eat_token(&str, mp_cmds[cmd_idx].name))
break;
+ cmd_idx++;
}
- if (mp_cmds[i].name == NULL)
+ if (mp_cmds[cmd_idx].name == NULL)
goto error;
- cmd_def = &mp_cmds[i];
-
cmd = talloc_ptrtype(NULL, cmd);
- *cmd = (mp_cmd_t){
- .id = cmd_def->id,
- .name = talloc_strdup(cmd, cmd_def->name),
- .pausing = pausing,
- .on_osd = on_osd,
- };
-
- ptr = str;
+ *cmd = mp_cmds[cmd_idx];
+ cmd->pausing = pausing;
+ cmd->on_osd = on_osd;
- for (i = 0; ptr && i < MP_CMD_MAX_ARGS; i++) {
- while (ptr[0] != ' ' && ptr[0] != '\t' && ptr[0] != '\0')
- ptr++;
- if (ptr[0] == '\0')
- break;
- while (ptr[0] == ' ' || ptr[0] == '\t')
- ptr++;
- if (ptr[0] == '\0' || ptr[0] == '#')
+ for (int i = 0; i < MP_CMD_MAX_ARGS; i++) {
+ if (!cmd->args[i].type)
break;
- cmd->args[i].type = cmd_def->args[i].type;
- switch (cmd_def->args[i].type) {
- case MP_CMD_ARG_INT:
- errno = 0;
- cmd->args[i].v.i = atoi(ptr);
- if (errno != 0) {
+ str = bstr_lstrip(str);
+ bstr arg = {0};
+ if (cmd->args[i].type == MP_CMD_ARG_STRING &&
+ bstr_eatstart0(&str, "\""))
+ {
+ if (!read_escaped_string(tmp, &str, &arg)) {
mp_tmsg(MSGT_INPUT, MSGL_ERR, "Command %s: argument %d "
- "isn't an integer.\n", cmd_def->name, i + 1);
+ "has broken string escapes.\n", cmd->name, i + 1);
goto error;
}
- break;
- case MP_CMD_ARG_FLOAT:
- errno = 0;
- cmd->args[i].v.f = atof(ptr);
- if (errno != 0) {
- mp_tmsg(MSGT_INPUT, MSGL_ERR, "Command %s: argument %d "
- "isn't a float.\n", cmd_def->name, i + 1);
+ if (!bstr_eatstart0(&str, "\"")) {
+ mp_tmsg(MSGT_INPUT, MSGL_ERR, "Command %s: argument %d is "
+ "unterminated.\n", cmd->name, i + 1);
goto error;
}
- break;
- case MP_CMD_ARG_STRING: {
- int term = ' ';
- if (*ptr == '\'' || *ptr == '"')
- term = *ptr++;
- char *argptr = talloc_size(cmd, strlen(ptr) + 1);
- cmd->args[i].v.s = argptr;
- while (1) {
- if (*ptr == 0) {
- if (term == ' ')
- break;
- mp_tmsg(MSGT_INPUT, MSGL_ERR, "Command %s: argument %d is "
- "unterminated.\n", cmd_def->name, i + 1);
- goto error;
- }
- if (*ptr == term)
- break;
- if (*ptr == '\\')
- ptr++;
- if (*ptr != 0)
- *argptr++ = *ptr++;
- }
- *argptr = 0;
- break;
+ } else {
+ if (!read_token(str, &str, &arg))
+ break;
}
- case 0:
- ptr = NULL;
- break;
- default:
- mp_tmsg(MSGT_INPUT, MSGL_ERR, "Unknown argument %d\n", i);
+ // Prevent option API from trying to deallocate static strings
+ cmd->args[i].v = ((struct mp_cmd_arg) {0}).v;
+ struct m_option opt = {0};
+ switch (cmd->args[i].type) {
+ case MP_CMD_ARG_INT: opt.type = &m_option_type_int; break;
+ case MP_CMD_ARG_FLOAT: opt.type = &m_option_type_float; break;
+ case MP_CMD_ARG_STRING: opt.type = &m_option_type_string; break;
+ default: abort();
+ }
+ int r = m_option_parse(&opt, bstr0(cmd->name), arg, &cmd->args[i].v);
+ if (r < 0) {
+ mp_tmsg(MSGT_INPUT, MSGL_ERR, "Command %s: argument %d "
+ "can't be parsed: %s.\n", cmd->name, i + 1,
+ m_option_strerror(r));
+ goto error;
}
+ if (opt.type == &m_option_type_string)
+ cmd->args[i].v.s = talloc_steal(cmd, cmd->args[i].v.s);
+ cmd->nargs++;
}
- cmd->nargs = i;
- int min_args;
- for (min_args = 0; min_args < MP_CMD_MAX_ARGS
- && cmd_def->args[min_args].type
- && !cmd_def->args[min_args].optional; min_args++);
- if (cmd->nargs < min_args) {
- mp_tmsg(MSGT_INPUT, MSGL_ERR, "Command \"%s\" requires at least %d "
- "arguments, we found only %d so far.\n", cmd_def->name,
- min_args, cmd->nargs);
+ bstr dummy;
+ if (read_token(str, &dummy, &dummy)) {
+ mp_tmsg(MSGT_INPUT, MSGL_ERR, "Command %s has trailing unused "
+ "arguments: '%.*s'.\n", cmd->name, BSTR_P(str));
+ // Better make it fatal to make it clear something is wrong.
goto error;
}
- for (; i < MP_CMD_MAX_ARGS && cmd_def->args[i].type; i++) {
- memcpy(&cmd->args[i], &cmd_def->args[i], sizeof(struct mp_cmd_arg));
- if (cmd_def->args[i].type == MP_CMD_ARG_STRING
- && cmd_def->args[i].v.s != NULL)
- cmd->args[i].v.s = talloc_strdup(cmd, cmd_def->args[i].v.s);
+ int min_args = 0;
+ while (min_args < MP_CMD_MAX_ARGS && cmd->args[min_args].type
+ && !cmd->args[min_args].optional)
+ {
+ min_args++;
+ }
+ if (cmd->nargs < min_args) {
+ mp_tmsg(MSGT_INPUT, MSGL_ERR, "Command %s requires at least %d "
+ "arguments, we found only %d so far.\n", cmd->name, min_args,
+ cmd->nargs);
+ goto error;
}
-
- if (i < MP_CMD_MAX_ARGS)
- cmd->args[i].type = 0;
talloc_free(tmp);
return cmd;
- error:
- mp_cmd_free(cmd);
+error:
+ talloc_free(cmd);
talloc_free(tmp);
return NULL;
}
diff --git a/m_property.c b/m_property.c
index 25c0ef51e8..68be17c974 100644
--- a/m_property.c
+++ b/m_property.c
@@ -144,37 +144,11 @@ char *m_properties_expand_string(const m_option_t *prop_list, char *str,
void *ctx)
{
int l, fr = 0, pos = 0, size = strlen(str) + 512;
- char *p = NULL, *e, *ret = malloc(size), num_val;
+ char *p = NULL, *e, *ret = malloc(size);
int skip = 0, lvl = 0, skip_lvl = 0;
while (str[0]) {
- if (str[0] == '\\') {
- int sl = 1;
- switch (str[1]) {
- case 'e':
- p = "\x1b", l = 1; break;
- case 'n':
- p = "\n", l = 1; break;
- case 'r':
- p = "\r", l = 1; break;
- case 't':
- p = "\t", l = 1; break;
- case 'x':
- if (str[2]) {
- char num[3] = { str[2], str[3], 0 };
- char *end = num;
- num_val = strtol(num, &end, 16);
- sl = end - num + 1;
- l = 1;
- p = &num_val;
- } else
- l = 0;
- break;
- default:
- p = str + 1, l = 1;
- }
- str += 1 + sl;
- } else if (lvl > 0 && str[0] == ')') {
+ if (lvl > 0 && str[0] == ')') {
if (skip && lvl <= skip_lvl)
skip = 0;
lvl--, str++, l = 0;