commands: change property expansion format string

This affects property format strings like they are used in the
"show_text" input command, for --playing-msg, and other places.

To quote the documentation comment on m_properties_expand_string():

    ${NAME} is expanded to the value of property NAME.
    If NAME starts with '=', use the raw value of the property.
    ${NAME:STR} expands to the property, or STR if the property is not
    available.
    ${?NAME:STR} expands to STR if the property is available.
    ${!NAME:STR} expands to STR if the property is not available.
    STR is recursively expanded using the same rules.
    "$$" can be used to escape "$", and "$}" to escape "}".
    "$>" disables parsing of "$" for the rest of the string.

Most importantly, "?(property:str)" becomes "${?property:str}".

Make the simple fallback case easier, e.g. "${property:fallback}"
instead of "${property}?(!property:fallback)".

Add the ability to escape the format meta characters. "$" is used for
escaping, because escaping with "\" is taken by the commands parser in
the layer below. "$>" can be used to disable interpretation of format
strings (of course escapes by the commands parser can't be canceled).

By default, properties which are unavailable or don't exist are turned
into a string signaling the status (e.g. "(unavailable)"), instead of
an empty string. If an empty string is desired, this has to be done
explicitly: "${property:}" (the fallback part is an empty string). Raw
properties still return an empty string on error.

m_properties_expand_string() now returns a talloc'ed pointer, instead of
a malloc'ed one.
This commit is contained in:
wm4 2012-09-23 23:00:54 +02:00
parent df2b0f9948
commit 32c5a87a01
6 changed files with 109 additions and 94 deletions

View File

@ -1525,7 +1525,7 @@ char *mp_property_print(const char *name, struct MPContext *ctx)
return ret;
}
char *property_expand_string(MPContext *mpctx, char *str)
char *mp_property_expand_string(struct MPContext *mpctx, char *str)
{
return m_properties_expand_string(mp_properties, str, mpctx);
}
@ -1931,15 +1931,13 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
}
case MP_CMD_SHOW_TEXT: {
char *txt = m_properties_expand_string(mp_properties,
cmd->args[0].v.s,
mpctx);
// if no argument supplied use default osd_duration, else <arg> ms.
char *txt = mp_property_expand_string(mpctx, cmd->args[0].v.s);
if (txt) {
// if no argument supplied use default osd_duration, else <arg> ms.
set_osd_msg(mpctx, OSD_MSG_TEXT, cmd->args[2].v.i,
(cmd->args[1].v.i < 0 ? osd_duration : cmd->args[1].v.i),
"%s", txt);
free(txt);
talloc_free(txt);
}
break;
}
@ -2198,10 +2196,10 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd)
case MP_CMD_RUN:
#ifndef __MINGW32__
if (!fork()) {
char *exp_cmd = property_expand_string(mpctx, cmd->args[0].v.s);
if (exp_cmd) {
execl("/bin/sh", "sh", "-c", exp_cmd, NULL);
free(exp_cmd);
char *s = mp_property_expand_string(mpctx, cmd->args[0].v.s);
if (s) {
execl("/bin/sh", "sh", "-c", s, NULL);
talloc_free(s);
}
exit(0);
}

View File

@ -23,7 +23,7 @@ struct MPContext;
struct mp_cmd;
void run_command(struct MPContext *mpctx, struct mp_cmd *cmd);
char *property_expand_string(struct MPContext *mpctx, char *str);
char *mp_property_expand_string(struct MPContext *mpctx, char *str);
void property_print_help(void);
int mp_property_do(const char* name, int action, void* val,
struct MPContext *mpctx);

View File

@ -27,6 +27,8 @@
#include <inttypes.h>
#include <assert.h>
#include <libavutil/common.h>
#include "talloc.h"
#include "m_option.h"
#include "m_property.h"
@ -140,73 +142,94 @@ int m_property_do(const m_option_t *prop_list, const char *name,
}
}
char *m_properties_expand_string(const m_option_t *prop_list, char *str,
static int m_property_do_bstr(const m_option_t *prop_list, bstr name,
int action, void *arg, void *ctx)
{
char name0[64];
if (name.len >= sizeof(name0))
return M_PROPERTY_UNKNOWN;
snprintf(name0, sizeof(name0), "%.*s", BSTR_P(name));
return m_property_do(prop_list, name0, action, arg, ctx);
}
static void append_str(char **s, int *len, bstr append)
{
MP_TARRAY_GROW(NULL, *s, *len + append.len);
memcpy(*s + *len, append.start, append.len);
*len = *len + append.len;
}
char *m_properties_expand_string(const m_option_t *prop_list, char *str0,
void *ctx)
{
int l, fr = 0, pos = 0, size = strlen(str) + 512;
char *p = NULL, *e, *ret = malloc(size);
int skip = 0, lvl = 0, skip_lvl = 0;
char *ret = NULL;
int ret_len = 0;
bool skip = false;
int level = 0, skip_level = 0;
bstr str = bstr0(str0);
while (str.len) {
if (level > 0 && bstr_eatstart0(&str, "}")) {
if (skip && level <= skip_level)
skip = false;
level--;
} else if (bstr_startswith0(str, "${") && bstr_find0(str, "}") >= 0) {
str = bstr_cut(str, 2);
level++;
// Assume ":" and "}" can't be part of the property name
// => if ":" comes before "}", it must be for the fallback
int term_pos = bstrcspn(str, ":}");
bstr name = bstr_splice(str, 0, term_pos < 0 ? str.len : term_pos);
str = bstr_cut(str, term_pos);
bool have_fallback = bstr_eatstart0(&str, ":");
while (str[0]) {
if (lvl > 0 && str[0] == ')') {
if (skip && lvl <= skip_lvl)
skip = 0;
lvl--, str++, l = 0;
} else if (str[0] == '$' && str[1] == '{'
&& (e = strchr(str + 2, '}'))) {
str += 2;
int method = M_PROPERTY_PRINT;
if (str[0] == '=') {
str += 1;
method = M_PROPERTY_GET_STRING;
}
int pl = e - str;
char pname[pl + 1];
memcpy(pname, str, pl);
pname[pl] = 0;
if (m_property_do(prop_list, pname, method, &p, ctx) >= 0 && p)
l = strlen(p), fr = 1;
else
l = 0;
str = e + 1;
} else if (str[0] == '?' && str[1] == '('
&& (e = strchr(str + 2, ':'))) {
lvl++;
if (!skip) {
int is_not = str[2] == '!';
int pl = e - str - (is_not ? 3 : 2);
char pname[pl + 1];
memcpy(pname, str + (is_not ? 3 : 2), pl);
pname[pl] = 0;
struct m_option opt = {0};
union m_option_value val = {0};
if (m_property_do(prop_list, pname, M_PROPERTY_GET_TYPE, &opt, ctx) <= 0 &&
m_property_do(prop_list, pname, M_PROPERTY_GET, &val, ctx) <= 0)
{
if (!is_not)
skip = 1, skip_lvl = lvl;
m_option_free(&opt, &val);
} else if (is_not)
skip = 1, skip_lvl = lvl;
bool cond_yes = bstr_eatstart0(&name, "?");
bool cond_no = !cond_yes && bstr_eatstart0(&name, "!");
bool raw = bstr_eatstart0(&name, "=");
int method = (raw || cond_yes || cond_no)
? M_PROPERTY_GET_STRING : M_PROPERTY_PRINT;
char *s = NULL;
int r = m_property_do_bstr(prop_list, name, method, &s, ctx);
if (cond_yes || cond_no) {
skip = (!!s != cond_yes);
} else {
skip = !!s;
char *append = s;
if (!s && !have_fallback && !raw) {
append = r == M_PROPERTY_UNAVAILABLE
? "(unavailable)" : "(error)";
}
append_str(&ret, &ret_len, bstr0(append));
}
talloc_free(s);
if (skip)
skip_level = level;
}
str = e + 1, l = 0;
} else
p = str, l = 1, str++;
} else if (level == 0 && bstr_eatstart0(&str, "$>")) {
append_str(&ret, &ret_len, str);
break;
} else {
char c;
if (skip || l <= 0)
continue;
// Other combinations, e.g. "$x", are added verbatim
if (bstr_eatstart0(&str, "$$")) {
c = '$';
} else if (bstr_eatstart0(&str, "$}")) {
c = '}';
} else {
c = str.start[0];
str = bstr_cut(str, 1);
}
if (pos + l + 1 > size) {
size = pos + l + 512;
ret = realloc(ret, size);
if (!skip)
MP_TARRAY_APPEND(NULL, ret, ret_len, c);
}
memcpy(ret + pos, p, l);
pos += l;
if (fr)
talloc_free(p), fr = 0;
}
ret[pos] = 0;
MP_TARRAY_APPEND(NULL, ret, ret_len, '\0');
return ret;
}

View File

@ -110,12 +110,18 @@ int m_property_do(const struct m_option* prop_list, const char* property_name,
void m_properties_print_help_list(const struct m_option* list);
// Expand a property string.
/* This function allows to print strings containing property values.
* ${NAME} is expanded to the value of property NAME or an empty
* string in case of error. $(NAME:STR) expand STR only if the property
* NAME is available.
*/
char* m_properties_expand_string(const struct m_option* prop_list, char* str,
// This function allows to print strings containing property values.
// ${NAME} is expanded to the value of property NAME.
// If NAME starts with '=', use the raw value of the property.
// ${NAME:STR} expands to the property, or STR if the property is not
// available.
// ${?NAME:STR} expands to STR if the property is available.
// ${!NAME:STR} expands to STR if the property is not available.
// General syntax: "${" ["?" | "!"] ["="] NAME ":" STR "}"
// STR is recursively expanded using the same rules.
// "$$" can be used to escape "$", and "$}" to escape "}".
// "$>" disables parsing of "$" for the rest of the string.
char* m_properties_expand_string(const struct m_option* prop_list, char *str,
void *ctx);
// Trivial helpers for implementing properties.

View File

@ -2172,10 +2172,9 @@ static void vo_update_window_title(struct MPContext *mpctx)
{
if (!mpctx->video_out)
return;
char *title = property_expand_string(mpctx, mpctx->opts.vo_wintitle);
char *title = mp_property_expand_string(mpctx, mpctx->opts.vo_wintitle);
talloc_free(mpctx->video_out->window_title);
mpctx->video_out->window_title = talloc_strdup(mpctx->video_out, title);
free(title);
mpctx->video_out->window_title = talloc_steal(mpctx, title);
}
int reinit_video_chain(struct MPContext *mpctx)
@ -3876,9 +3875,9 @@ goto_enable_cache:
#endif
if (opts->playing_msg) {
char *msg = property_expand_string(mpctx, opts->playing_msg);
char *msg = mp_property_expand_string(mpctx, opts->playing_msg);
mp_msg(MSGT_CPLAYER, MSGL_INFO, "%s", msg);
free(msg);
talloc_free(msg);
}
// Disable the term OSD in verbose mode

View File

@ -66,19 +66,6 @@ static char *stripext(void *talloc_ctx, const char *s)
return talloc_asprintf(talloc_ctx, "%.*s", end - s, s);
}
static char *do_format_property(struct MPContext *mpctx, struct bstr s) {
struct bstr prop_name = s;
int fallbackpos = bstrchr(s, ':');
if (fallbackpos >= 0)
prop_name = bstr_splice(prop_name, 0, fallbackpos);
char *pn = bstrdup0(NULL, prop_name);
char *res = mp_property_print(pn, mpctx);
talloc_free(pn);
if (!res && fallbackpos >= 0)
res = bstrdup0(NULL, bstr_cut(s, fallbackpos + 1));
return res;
}
#ifdef _WIN32
#define ILLEGAL_FILENAME_CHARS "?\"/\\<>*|:"
#else
@ -186,11 +173,13 @@ static char *create_fname(struct MPContext *mpctx, char *template,
if (!end)
goto error_exit;
struct bstr prop = bstr_splice(bstr0(template), 0, end - template);
template = end + 1;
char *s = do_format_property(mpctx, prop);
char *tmp = talloc_asprintf(NULL, "${%.*s}", BSTR_P(prop));
char *s = mp_property_expand_string(mpctx, tmp);
talloc_free(tmp);
if (s)
append_filename(&res, s);
talloc_free(s);
template = end + 1;
break;
}
case '%':