From d2c409c56b49252c85eefd340863fd59a36808d6 Mon Sep 17 00:00:00 2001 From: Dudemanguy Date: Sat, 25 Jan 2025 19:33:08 -0600 Subject: [PATCH] m_option: reintroduce -del to string list and keyvalue list b56e63e2a96b67aff4050d4db06ee67665893c36 removed this because it was deprecated and not clearly useful. This commit adds this operation back to string lists and keyvalue lists, but with one important change. It operates via the actual values and not indexes. So you can use --foo-del=bar,bar2 to remove bar and bar2 from foo. The difference from using -remove is that this is subject to escaping rules and has the same caveats as -add. Note that -del wasn't added back to the object settings list because you can already remove multiple items with -remove from it. --- DOCS/interface-changes/list-option.txt | 1 + DOCS/man/mpv.rst | 2 + options/m_option.c | 160 ++++++++++++++++--------- 3 files changed, 105 insertions(+), 58 deletions(-) diff --git a/DOCS/interface-changes/list-option.txt b/DOCS/interface-changes/list-option.txt index ec74d13ef9..98785ad4e2 100644 --- a/DOCS/interface-changes/list-option.txt +++ b/DOCS/interface-changes/list-option.txt @@ -1 +1,2 @@ undeprecate list option suffixes that work with multiple items +add `-del` to string list and keyvalue list options diff --git a/DOCS/man/mpv.rst b/DOCS/man/mpv.rst index 5e40f4c4cf..4f54908247 100644 --- a/DOCS/man/mpv.rst +++ b/DOCS/man/mpv.rst @@ -653,6 +653,7 @@ Suffix Meaning -add Append 1 or more items (same syntax as -set) -pre Prepend 1 or more items (same syntax as -set) -clr Clear the option (remove all items) +-del Delete 1 or more items if present (same syntax as -set) -remove Delete item if present (does not interpret escapes) -toggle Append an item, or remove it if it already exists (no escapes) ============= =============================================== @@ -675,6 +676,7 @@ Suffix Meaning -set Set a list of items (using ``,`` as separator) -append Append a single item (escapes for the key, no escapes for the value) -add Append 1 or more items (same syntax as -set) +-del Delete 1 or more keys if present (same syntax as -set) -remove Delete item by key if present (does not interpret escapes) ============= =============================================== diff --git a/options/m_option.c b/options/m_option.c index 0416ab2e33..c72257e5be 100644 --- a/options/m_option.c +++ b/options/m_option.c @@ -1319,9 +1319,10 @@ const m_option_type_t m_option_type_string = { #define OP_ADD 1 #define OP_PRE 2 #define OP_CLR 3 -#define OP_TOGGLE 4 -#define OP_APPEND 5 -#define OP_REMOVE 6 +#define OP_DEL 4 +#define OP_TOGGLE 5 +#define OP_APPEND 6 +#define OP_REMOVE 7 static void free_str_list(void *dst) { @@ -1403,23 +1404,84 @@ static int find_list_bstr(char **list, bstr item) return -1; } +static char **separate_input_param(const m_option_t *opt, bstr param, + int *len, int op) +{ + char separator = opt->priv ? *(char *)opt->priv : OPTION_LIST_SEPARATOR; + if (op == OP_REMOVE) + separator = 0; // specially handled + struct bstr str = param; + int n = *len; + while (str.len) { + get_nextsep(&str, separator, 0); + str = bstr_cut(str, 1); + n++; + } + if (n == 0) + return NULL; + + char **list = talloc_array(NULL, char *, n + 2); + str = bstrdup(NULL, param); + char *ptr = str.start; + n = 0; + + while (1) { +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + if (n >= 100) + break; +#endif + struct bstr el = get_nextsep(&str, separator, 1); + list[n] = bstrdup0(NULL, el); + n++; + if (!str.len) + break; + str = bstr_cut(str, 1); + } + list[n] = NULL; + *len = n; + talloc_free(ptr); + return list; +} + +static int str_list_remove(char **remove, int n, void *dst) +{ + bool found = false; + char **list = VAL(dst); + for (int i = 0; i < n; i++) { + int index = 0; + do { + index = find_list_bstr(list, bstr0(remove[i])); + if (index >= 0) { + found = true; + char *old = list[index]; + for (int j = index; list[j]; j++) + list[j] = list[j + 1]; + talloc_free(old); + } + } while (index >= 0); + talloc_free(remove[i]); + } + talloc_free(remove); + return found; +} + static int parse_str_list_impl(struct mp_log *log, const m_option_t *opt, struct bstr name, struct bstr param, void *dst, int default_op) { char **res; int op = default_op; - bool multi = true; if (bstr_endswith0(name, "-add")) { op = OP_ADD; } else if (bstr_endswith0(name, "-append")) { - op = OP_ADD; - multi = false; + op = OP_APPEND; } else if (bstr_endswith0(name, "-pre")) { op = OP_PRE; } else if (bstr_endswith0(name, "-clr")) { op = OP_CLR; + } else if (bstr_endswith0(name, "-del")) { + op = OP_DEL; } else if (bstr_endswith0(name, "-set")) { op = OP_NONE; } else if (bstr_endswith0(name, "-toggle")) { @@ -1430,26 +1492,16 @@ static int parse_str_list_impl(struct mp_log *log, const m_option_t *opt, if (op == OP_TOGGLE || op == OP_REMOVE) { if (dst) { - char **list = VAL(dst); - bool found = false; - int index = 0; - do { - index = find_list_bstr(list, param); - if (index >= 0) { - found = true; - char *old = list[index]; - for (int n = index; list[n]; n++) - list[n] = list[n + 1]; - talloc_free(old); - } - } while (index >= 0); + res = talloc_array(NULL, char *, 2); + res[0] = bstrdup0(res, param); + res[1] = NULL; + bool found = str_list_remove(res, 2, dst); if (found) return 1; } if (op == OP_REMOVE) return 1; // ignore if not found - op = OP_ADD; - multi = false; + op = OP_APPEND; } // Clear the list ?? @@ -1463,47 +1515,22 @@ static int parse_str_list_impl(struct mp_log *log, const m_option_t *opt, if (param.len == 0 && op != OP_NONE) return M_OPT_MISSING_PARAM; - char separator = opt->priv ? *(char *)opt->priv : OPTION_LIST_SEPARATOR; - if (!multi) - separator = 0; // specially handled - int n = 0; - struct bstr str = param; - while (str.len) { - get_nextsep(&str, separator, 0); - str = bstr_cut(str, 1); - n++; - } - if (n == 0 && op != OP_NONE) - return M_OPT_INVALID; - if (!dst) return 1; - res = talloc_array(NULL, char *, n + 2); - str = bstrdup(NULL, param); - char *ptr = str.start; - n = 0; - - while (1) { -#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - if (n >= 100) - break; -#endif - struct bstr el = get_nextsep(&str, separator, 1); - res[n] = bstrdup0(NULL, el); - n++; - if (!str.len) - break; - str = bstr_cut(str, 1); - } - res[n] = NULL; - talloc_free(ptr); + int n = 0; + res = separate_input_param(opt, param, &n, op); + if (!res) + return M_OPT_INVALID; switch (op) { case OP_ADD: + case OP_APPEND: return str_list_add(res, n, dst, 0); case OP_PRE: return str_list_add(res, n, dst, 1); + case OP_DEL: + return str_list_remove(res, n, dst); } if (VAL(dst)) @@ -1634,6 +1661,7 @@ const m_option_type_t m_option_type_string_list = { {"add"}, {"append"}, {"clr", M_OPT_TYPE_OPTIONAL_PARAM}, + {"del"}, {"pre"}, {"set"}, {"toggle"}, @@ -1679,17 +1707,32 @@ static int parse_keyvalue_list(struct mp_log *log, const m_option_t *opt, if ((opt->flags & M_OPT_HAVE_HELP) && bstr_equals0(param, "help")) param = bstr0("help="); + int op = 0; + if (bstr_endswith0(name, "-del")) { + op = OP_DEL; + } else if (bstr_endswith0(name, "-remove")) { + op = OP_REMOVE; + } + if (bstr_endswith0(name, "-add")) { append = true; } else if (bstr_endswith0(name, "-append")) { append = full_value = true; - } else if (bstr_endswith0(name, "-remove")) { + } else if (op == OP_DEL || op == OP_REMOVE) { + int n = 0; + char **res = separate_input_param(opt, param, &n, op); + if (!res) + return M_OPT_INVALID; lst = dst ? VAL(dst) : NULL; - int index = dst ? keyvalue_list_find_key(lst, param) : -1; - if (index >= 0) { - keyvalue_list_del_key(lst, index); - VAL(dst) = lst; + for (int i = 0; i < n; i++) { + int index = dst ? keyvalue_list_find_key(lst, bstr0(res[i])) : -1; + if (index >= 0) { + keyvalue_list_del_key(lst, index); + VAL(dst) = lst; + } + talloc_free(res[i]); } + talloc_free(res); return 1; } @@ -1818,6 +1861,7 @@ const m_option_type_t m_option_type_keyvalue_list = { .actions = (const struct m_option_action[]){ {"add"}, {"append"}, + {"del"}, {"set"}, {"remove"}, {0}