diff --git a/cmd-set-option.c b/cmd-set-option.c index 5b5ea3d88..1b6de1e86 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -178,10 +178,10 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) struct window_pane *loop; struct options *oo; struct options_entry *parent, *o, *po; - char *name, *argument, *expanded = NULL; - char *cause; + char *name, *argument, *cause; + char *expanded = NULL, *array_key = NULL; const char *value; - int window, idx, already, error, ambiguous; + int window, already, error, ambiguous; int scope; window = (cmd_get_entry(self) == &cmd_set_window_option_entry); @@ -202,8 +202,8 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); } - /* Parse option name and index. */ - name = options_match(argument, &idx, &ambiguous); + /* Parse option name and array key. */ + name = options_match(argument, &array_key, &ambiguous); if (name == NULL) { if (args_has(args, 'q')) goto out; @@ -235,21 +235,23 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) o = options_get_only(oo, name); parent = options_get(oo, name); - /* Check that array options and indexes match up. */ - if (idx != -1 && (*name == '@' || !options_is_array(parent))) { + /* Check that array options and keys match up. */ + if (array_key != NULL && (*name == '@' || !options_is_array(parent))) { cmdq_error(item, "not an array: %s", argument); goto fail; } /* With -o, check this option is not already set. */ if (!args_has(args, 'u') && args_has(args, 'o')) { - if (idx == -1) + if (array_key == NULL) already = (o != NULL); else { if (o == NULL) already = 0; + else if (options_array_get(o, array_key) != NULL) + already = 1; else - already = (options_array_get(o, idx) != NULL); + already = 0; } if (already) { if (args_has(args, 'q')) @@ -265,7 +267,8 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) po = options_get_only(loop->options, name); if (po == NULL) continue; - if (options_remove_or_default(po, idx, &cause) != 0) { + if (options_remove_or_default(po, array_key, + &cause) != 0) { cmdq_error(item, "%s", cause); free(cause); goto fail; @@ -275,7 +278,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) if (args_has(args, 'u') || args_has(args, 'U')) { if (o == NULL) goto out; - if (options_remove_or_default(o, idx, &cause) != 0) { + if (options_remove_or_default(o, array_key, &cause) != 0) { cmdq_error(item, "%s", cause); free(cause); goto fail; @@ -286,7 +289,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) goto fail; } options_set_string(oo, name, append, "%s", value); - } else if (idx == -1 && !options_is_array(parent)) { + } else if (array_key == NULL && !options_is_array(parent)) { error = options_from_string(oo, options_table_entry(parent), options_table_entry(parent)->name, value, args_has(args, 'a'), &cause); @@ -302,7 +305,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) } if (o == NULL) o = options_empty(oo, options_table_entry(parent)); - if (idx == -1) { + if (array_key == NULL) { if (!append) options_array_clear(o); if (options_array_assign(o, value, &cause) != 0) { @@ -310,7 +313,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) free(cause); goto fail; } - } else if (options_array_set(o, idx, value, append, + } else if (options_array_set(o, array_key, value, append, &cause) != 0) { cmdq_error(item, "%s", cause); free(cause); @@ -324,11 +327,13 @@ out: free(argument); free(expanded); free(name); + free(array_key); return (CMD_RETURN_NORMAL); fail: free(argument); free(expanded); free(name); + free(array_key); return (CMD_RETURN_ERROR); } diff --git a/format.c b/format.c index 6757a4276..f2bc22a7a 100644 --- a/format.c +++ b/format.c @@ -4213,25 +4213,26 @@ format_find(struct format_tree *ft, const char *key, uint64_t modifiers, struct format_entry *fe, fe_find; struct environ_entry *envent; struct options_entry *o; - int idx; char *found = NULL, *saved, s[512]; + char *array_key = NULL; const char *errstr; time_t t = 0; struct tm tm; - o = options_parse_get(global_options, key, &idx, 0); + o = options_parse_get(global_options, key, &array_key, 0); if (o == NULL && ft->wp != NULL) - o = options_parse_get(ft->wp->options, key, &idx, 0); + o = options_parse_get(ft->wp->options, key, &array_key, 0); if (o == NULL && ft->w != NULL) - o = options_parse_get(ft->w->options, key, &idx, 0); + o = options_parse_get(ft->w->options, key, &array_key, 0); if (o == NULL) - o = options_parse_get(global_w_options, key, &idx, 0); + o = options_parse_get(global_w_options, key, &array_key, 0); if (o == NULL && ft->s != NULL) - o = options_parse_get(ft->s->options, key, &idx, 0); + o = options_parse_get(ft->s->options, key, &array_key, 0); if (o == NULL) - o = options_parse_get(global_s_options, key, &idx, 0); + o = options_parse_get(global_s_options, key, &array_key, 0); if (o != NULL) { - found = options_to_string(o, idx, 1); + found = options_to_string(o, array_key, 1); + free(array_key); goto found; } @@ -4925,7 +4926,7 @@ format_add_window_neighbour(struct format_tree *nft, struct winlink *wl, oname = options_name(o); if (*oname == '@') { xasprintf(&prefixed, "%s_%s", prefix, oname); - oval = options_to_string(o, -1, 1); + oval = options_to_string(o, NULL, 1); format_add(nft, prefixed, "%s", oval); free(oval); free(prefixed); @@ -5097,11 +5098,12 @@ format_loop_add_option(struct format_expand_state *es, const char *fmt, nft = format_create(ft->client, ft->item, FORMAT_NONE, ft->flags); format_add(nft, "option_name", "%s", name); - s = options_to_string(o, -1, 0); + s = options_to_string(o, NULL, 0); format_add(nft, "option_value", "%s", s); free(s); format_add(nft, "option_is_array", "%d", is_array); + format_add(nft, "option_array_key", "%s", ""); format_add(nft, "option_array_index", "%s", ""); format_add(nft, "option_array_first", "%d", is_array); format_add(nft, "option_array_last", "%d", is_array); @@ -5139,20 +5141,21 @@ format_loop_add_array_item(struct format_expand_state *es, const char *fmt, struct format_expand_state next; const struct options_table_entry *oe = options_table_entry(o); const char *name = options_name(o); + const char *array_key; char *expanded, *s; - u_int idx; - idx = options_array_item_index(a); - format_log(es, "option loop: %s[%u]", name, idx); + array_key = options_array_item_key(a); + format_log(es, "option loop: %s[%s]", name, array_key); nft = format_create(ft->client, ft->item, FORMAT_NONE, ft->flags); format_add(nft, "option_name", "%s", name); - s = options_to_string(o, idx, 0); + s = options_to_string(o, array_key, 0); format_add(nft, "option_value", "%s", s); free(s); format_add(nft, "option_is_array", "1"); - format_add(nft, "option_array_index", "%u", idx); + format_add(nft, "option_array_key", "%s", array_key); + format_add(nft, "option_array_index", "%s", array_key); if (a == options_array_first(o)) format_add(nft, "option_array_first", "1"); else diff --git a/options.c b/options.c index 19da38886..fd5f1b262 100644 --- a/options.c +++ b/options.c @@ -32,18 +32,69 @@ */ struct options_array_item { - u_int index; + char *key; union options_value value; RB_ENTRY(options_array_item) entry; }; + +static int +options_array_key_to_number(const char *key, u_int *idx) +{ + const char *errstr; + long long n; + + if (*key == '\0') + return (-1); + for (const char *cp = key; *cp != '\0'; cp++) { + if (!isdigit((u_char)*cp)) + return (0); + } + + n = strtonum(key, 0, UINT_MAX, &errstr); + if (errstr != NULL) + return (-1); + if (idx != NULL) + *idx = n; + return (1); +} + +static char * +options_array_correct_key(const char *key) +{ + u_int idx; + int numeric; + char *out; + + numeric = options_array_key_to_number(key, &idx); + if (numeric == -1) + return (NULL); + if (numeric == 1) { + xasprintf(&out, "%u", idx); + return (out); + } + return (xstrdup(key)); +} + static int options_array_cmp(struct options_array_item *a1, struct options_array_item *a2) { - if (a1->index < a2->index) + u_int i1, i2; + int n1, n2; + + n1 = options_array_key_to_number(a1->key, &i1); + n2 = options_array_key_to_number(a2->key, &i2); + if (n1 && n2) { + if (i1 < i2) + return (-1); + if (i1 > i2) + return (1); + return (0); + } + if (n1) return (-1); - if (a1->index > a2->index) + if (n2) return (1); - return (0); + return (strcmp(a1->key, a2->key)); } RB_GENERATE_STATIC(options_array, options_array_item, entry, options_array_cmp); @@ -260,6 +311,7 @@ options_default(struct options *oo, const struct options_table_entry *oe) { struct options_entry *o; union options_value *ov; + char key[32]; u_int i; struct cmd_parse_result *pr; @@ -271,8 +323,10 @@ options_default(struct options *oo, const struct options_table_entry *oe) options_array_assign(o, oe->default_str, NULL); return (o); } - for (i = 0; oe->default_arr[i] != NULL; i++) - options_array_set(o, i, oe->default_arr[i], 0, NULL); + for (i = 0; oe->default_arr[i] != NULL; i++) { + xsnprintf(key, sizeof key, "%u", i); + options_array_set(o, key, oe->default_arr[i], 0, NULL); + } return (o); } @@ -393,21 +447,21 @@ options_table_entry(struct options_entry *o) } static struct options_array_item * -options_array_item(struct options_entry *o, u_int idx) +options_array_item(struct options_entry *o, const char *key) { struct options_array_item a; - a.index = idx; + a.key = (char *)key; return (RB_FIND(options_array, &o->value.array, &a)); } static struct options_array_item * -options_array_new(struct options_entry *o, u_int idx) +options_array_new(struct options_entry *o, const char *key) { struct options_array_item *a; a = xcalloc(1, sizeof *a); - a->index = idx; + a->key = xstrdup(key); RB_INSERT(options_array, &o->value.array, a); return (a); } @@ -417,6 +471,7 @@ options_array_free(struct options_entry *o, struct options_array_item *a) { options_value_free(o, &a->value); RB_REMOVE(options_array, &o->value.array, a); + free(a->key); free(a); } @@ -433,24 +488,45 @@ options_array_clear(struct options_entry *o) } union options_value * -options_array_get(struct options_entry *o, u_int idx) +options_array_get(struct options_entry *o, const char *key) { struct options_array_item *a; + char *new_key; if (!OPTIONS_IS_ARRAY(o)) return (NULL); - a = options_array_item(o, idx); + new_key = options_array_correct_key(key); + if (new_key == NULL) + return (NULL); + a = options_array_item(o, new_key); + free(new_key); if (a == NULL) return (NULL); return (&a->value); } +union options_value * +options_array_getv(struct options_entry *o, const char *fmt, ...) +{ + union options_value *ov; + va_list ap; + char *key; + + va_start(ap, fmt); + xvasprintf(&key, fmt, ap); + va_end(ap); + + ov = options_array_get(o, key); + free(key); + return (ov); +} + int -options_array_set(struct options_entry *o, u_int idx, const char *value, +options_array_set(struct options_entry *o, const char *key, const char *value, int append, char **cause) { struct options_array_item *a; - char *new; + char *new, *new_key; struct cmd_parse_result *pr; long long number; @@ -460,10 +536,18 @@ options_array_set(struct options_entry *o, u_int idx, const char *value, return (-1); } + new_key = options_array_correct_key(key); + if (new_key == NULL) { + if (cause != NULL) + xasprintf(cause, "bad array key: %s", key); + return (-1); + } + if (value == NULL) { - a = options_array_item(o, idx); + a = options_array_item(o, new_key); if (a != NULL) options_array_free(o, a); + free(new_key); return (0); } @@ -475,50 +559,56 @@ options_array_set(struct options_entry *o, u_int idx, const char *value, *cause = pr->error; else free(pr->error); + free(new_key); return (-1); case CMD_PARSE_SUCCESS: break; } - a = options_array_item(o, idx); + a = options_array_item(o, new_key); if (a == NULL) - a = options_array_new(o, idx); + a = options_array_new(o, new_key); else options_value_free(o, &a->value); a->value.cmdlist = pr->cmdlist; + free(new_key); return (0); } if (OPTIONS_IS_STRING(o)) { - a = options_array_item(o, idx); + a = options_array_item(o, new_key); if (a != NULL && append) xasprintf(&new, "%s%s", a->value.string, value); else new = xstrdup(value); if (a == NULL) - a = options_array_new(o, idx); + a = options_array_new(o, new_key); else options_value_free(o, &a->value); a->value.string = new; + free(new_key); return (0); } if (o->tableentry->type == OPTIONS_TABLE_COLOUR) { if ((number = colour_fromstring(value)) == -1) { xasprintf(cause, "bad colour: %s", value); + free(new_key); return (-1); } - a = options_array_item(o, idx); + a = options_array_item(o, new_key); if (a == NULL) - a = options_array_new(o, idx); + a = options_array_new(o, new_key); else options_value_free(o, &a->value); a->value.number = number; + free(new_key); return (0); } if (cause != NULL) *cause = xstrdup("wrong array type"); + free(new_key); return (-1); } @@ -527,6 +617,7 @@ options_array_assign(struct options_entry *o, const char *s, char **cause) { const char *separator; char *copy, *next, *string; + char key[32]; u_int i; separator = o->tableentry->separator; @@ -536,10 +627,11 @@ options_array_assign(struct options_entry *o, const char *s, char **cause) if (*s == '\0') return (0); for (i = 0; i < UINT_MAX; i++) { - if (options_array_item(o, i) == NULL) + if (options_array_getv(o, "%u", i) == NULL) break; } - return (options_array_set(o, i, s, 0, cause)); + xsnprintf(key, sizeof key, "%u", i); + return (options_array_set(o, key, s, 0, cause)); } if (*s == '\0') @@ -549,12 +641,13 @@ options_array_assign(struct options_entry *o, const char *s, char **cause) if (*next == '\0') continue; for (i = 0; i < UINT_MAX; i++) { - if (options_array_item(o, i) == NULL) + if (options_array_getv(o, "%u", i) == NULL) break; } if (i == UINT_MAX) break; - if (options_array_set(o, i, next, 0, cause) != 0) { + xsnprintf(key, sizeof key, "%u", i); + if (options_array_set(o, key, next, 0, cause) != 0) { free(copy); return (-1); } @@ -577,10 +670,10 @@ options_array_next(struct options_array_item *a) return (RB_NEXT(options_array, &o->value.array, a)); } -u_int -options_array_item_index(struct options_array_item *a) +const char * +options_array_item_key(struct options_array_item *a) { - return (a->index); + return (a->key); } union options_value * @@ -602,15 +695,16 @@ options_is_string(struct options_entry *o) } char * -options_to_string(struct options_entry *o, int idx, int numeric) +options_to_string(struct options_entry *o, const char *key, int numeric) { struct options_array_item *a; char *result = NULL; char *last = NULL; char *next; + char *new_key; if (OPTIONS_IS_ARRAY(o)) { - if (idx == -1) { + if (key == NULL) { RB_FOREACH(a, options_array, &o->value.array) { next = options_value_to_string(o, &a->value, numeric); @@ -627,7 +721,11 @@ options_to_string(struct options_entry *o, int idx, int numeric) return (xstrdup("")); return (result); } - a = options_array_item(o, idx); + new_key = options_array_correct_key(key); + if (new_key == NULL) + return (xstrdup("")); + a = options_array_item(o, new_key); + free(new_key); if (a == NULL) return (xstrdup("")); return (options_value_to_string(o, &a->value, numeric)); @@ -636,37 +734,41 @@ options_to_string(struct options_entry *o, int idx, int numeric) } char * -options_parse(const char *name, int *idx) +options_parse(const char *name, char **key) { - char *copy, *cp, *end; + char *copy, *cp, *end, *raw, *new_key; if (*name == '\0') return (NULL); + *key = NULL; copy = xstrdup(name); if ((cp = strchr(copy, '[')) == NULL) { - *idx = -1; return (copy); } end = strchr(cp + 1, ']'); - if (end == NULL || end[1] != '\0' || !isdigit((u_char)end[-1])) { + if (end == NULL || end[1] != '\0' || end == cp + 1) { free(copy); return (NULL); } - if (sscanf(cp, "[%d]", idx) != 1 || *idx < 0) { + raw = xstrndup(cp + 1, end - (cp + 1)); + new_key = options_array_correct_key(raw); + free(raw); + if (new_key == NULL) { free(copy); return (NULL); } + *key = new_key; *cp = '\0'; return (copy); } struct options_entry * -options_parse_get(struct options *oo, const char *s, int *idx, int only) +options_parse_get(struct options *oo, const char *s, char **key, int only) { struct options_entry *o; char *name; - name = options_parse(s, idx); + name = options_parse(s, key); if (name == NULL) return (NULL); if (only) @@ -674,6 +776,10 @@ options_parse_get(struct options *oo, const char *s, int *idx, int only) else o = options_get(oo, name); free(name); + if (o == NULL) { + free(*key); + *key = NULL; + } return (o); } @@ -690,14 +796,14 @@ options_search(const char *name) } char * -options_match(const char *s, int *idx, int *ambiguous) +options_match(const char *s, char **key, int *ambiguous) { const struct options_table_entry *oe, *found; char *parsed; const char *name; size_t namelen; - parsed = options_parse(s, idx); + parsed = options_parse(s, key); if (parsed == NULL) return (NULL); if (*parsed == '@') { @@ -718,6 +824,8 @@ options_match(const char *s, int *idx, int *ambiguous) if (found != NULL) { *ambiguous = 1; free(parsed); + free(*key); + *key = NULL; return (NULL); } found = oe; @@ -726,19 +834,21 @@ options_match(const char *s, int *idx, int *ambiguous) free(parsed); if (found == NULL) { *ambiguous = 0; + free(*key); + *key = NULL; return (NULL); } return (xstrdup(found->name)); } struct options_entry * -options_match_get(struct options *oo, const char *s, int *idx, int only, +options_match_get(struct options *oo, const char *s, char **key, int only, int *ambiguous) { char *name; struct options_entry *o; - name = options_match(s, idx, ambiguous); + name = options_match(s, key, ambiguous); if (name == NULL) return (NULL); *ambiguous = 0; @@ -747,6 +857,10 @@ options_match_get(struct options *oo, const char *s, int *idx, int only, else o = options_get(oo, name); free(name); + if (o == NULL) { + free(*key); + *key = NULL; + } return (o); } @@ -1340,11 +1454,12 @@ options_push_changes(const char *name) } int -options_remove_or_default(struct options_entry *o, int idx, char **cause) +options_remove_or_default(struct options_entry *o, const char *key, + char **cause) { struct options *oo = o->owner; - if (idx == -1) { + if (key == NULL) { if (o->tableentry != NULL && (oo == global_options || oo == global_s_options || @@ -1352,7 +1467,7 @@ options_remove_or_default(struct options_entry *o, int idx, char **cause) options_default(oo, o->tableentry); else options_remove(o); - } else if (options_array_set(o, idx, NULL, 0, cause) != 0) + } else if (options_array_set(o, key, NULL, 0, cause) != 0) return (-1); return (0); } diff --git a/window-customize.c b/window-customize.c index 32caae9a3..09da37f2e 100644 --- a/window-customize.c +++ b/window-customize.c @@ -82,15 +82,15 @@ enum window_customize_change { }; struct window_customize_itemdata { - struct window_customize_modedata *data; - enum window_customize_scope scope; + struct window_customize_modedata *data; + enum window_customize_scope scope; - char *table; - key_code key; + char *table; + key_code key; - struct options *oo; - char *name; - int idx; + struct options *oo; + char *name; + char *array_key; }; struct window_customize_modedata { @@ -111,15 +111,17 @@ struct window_customize_modedata { }; static uint64_t -window_customize_get_tag(struct options_entry *o, int idx, +window_customize_get_tag(struct options_entry *o, struct options_array_item *a, const struct options_table_entry *oe) { uint64_t offset; + if (a != NULL) + return ((uint64_t)(uintptr_t)a); if (oe == NULL) return ((uint64_t)o); offset = ((char *)oe - (char *)options_table) / sizeof *options_table; - return ((2ULL << 62)|(offset << 32)|((idx + 1) << 1)|1); + return ((2ULL << 62)|(offset << 32)|1); } static struct options * @@ -224,6 +226,7 @@ window_customize_free_item(struct window_customize_itemdata *item) { free(item->table); free(item->name); + free(item->array_key); free(item); } @@ -237,26 +240,26 @@ window_customize_build_array(struct window_customize_modedata *data, struct window_customize_itemdata *item; struct options_array_item *ai; char *name, *value, *text; - u_int idx; uint64_t tag; + const char *array_key; ai = options_array_first(o); while (ai != NULL) { - idx = options_array_item_index(ai); + array_key = options_array_item_key(ai); - xasprintf(&name, "%s[%u]", options_name(o), idx); + xasprintf(&name, "%s[%s]", options_name(o), array_key); format_add(ft, "option_name", "%s", name); - value = options_to_string(o, idx, 0); + value = options_to_string(o, array_key, 0); format_add(ft, "option_value", "%s", value); item = window_customize_add_item(data); item->scope = scope; item->oo = oo; item->name = xstrdup(options_name(o)); - item->idx = idx; + item->array_key = xstrdup(array_key); text = format_expand(ft, data->format); - tag = window_customize_get_tag(o, idx, oe); + tag = window_customize_get_tag(o, ai, oe); mode_tree_add(data->data, top, item, tag, name, text, -1); free(text); @@ -307,7 +310,7 @@ window_customize_build_option(struct window_customize_modedata *data, format_add(ft, "option_unit", "%s", ""); if (!array) { - value = options_to_string(o, -1, 0); + value = options_to_string(o, NULL, 0); format_add(ft, "option_value", "%s", value); free(value); } @@ -324,13 +327,12 @@ window_customize_build_option(struct window_customize_modedata *data, item->oo = oo; item->scope = scope; item->name = xstrdup(name); - item->idx = -1; if (array) text = NULL; else text = format_expand(ft, data->format); - tag = window_customize_get_tag(o, -1, oe); + tag = window_customize_get_tag(o, NULL, oe); top = mode_tree_add(data->data, top, item, tag, name, text, 0); free(text); @@ -483,7 +485,6 @@ window_customize_build_keys(struct window_customize_modedata *data, item->table = xstrdup(kt->name); item->key = bd->key; item->name = xstrdup(key_string_lookup_key(item->key, 0)); - item->idx = -1; expanded = format_expand(ft, data->format); child = mode_tree_add(data->data, top, item, (uint64_t)bd, @@ -653,12 +654,12 @@ window_customize_draw_option(struct window_customize_modedata *data, { struct screen *s = ctx->s; u_int cx = s->cx, cy = s->cy; - int idx; struct options_entry *o, *parent; struct options *go, *wo; const struct options_table_entry *oe; struct grid_cell gc; const char **choice, *text, *name; + const char *array_key; const char *space = "", *unit = ""; char *value = NULL, *expanded; char *default_value = NULL; @@ -669,7 +670,7 @@ window_customize_draw_option(struct window_customize_modedata *data, if (!window_customize_check_item(data, item, &fs)) return; name = item->name; - idx = item->idx; + array_key = item->array_key; o = options_get(item->oo, name); if (o == NULL) @@ -708,25 +709,25 @@ window_customize_draw_option(struct window_customize_modedata *data, &grid_default_cell, "This is a %s option.", text)) goto out; if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) { - if (idx != -1) { + if (array_key != NULL) { if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, &grid_default_cell, - "This is an array option, index %u.", idx)) + "This is an array option, key %s.", array_key)) goto out; } else { if (!screen_write_text(ctx, cx, sx, sy - (s->cy - cy), 0, &grid_default_cell, "This is an array option.")) goto out; } - if (idx == -1) + if (array_key == NULL) goto out; } screen_write_cursormove(ctx, cx, s->cy + 1, 0); /* skip line */ if (s->cy >= cy + sy - 1) goto out; - value = options_to_string(o, idx, 0); - if (oe != NULL && idx == -1) { + value = options_to_string(o, array_key, 0); + if (oe != NULL && array_key == NULL) { default_value = options_default_to_string(oe); if (strcmp(default_value, value) == 0) { free(default_value); @@ -820,7 +821,7 @@ window_customize_draw_option(struct window_customize_modedata *data, if (wo != NULL && options_owner(o) != wo) { parent = options_get_only(wo, name); if (parent != NULL) { - value = options_to_string(parent, -1 , 0); + value = options_to_string(parent, NULL, 0); if (!screen_write_text(ctx, s->cx, sx, sy - (s->cy - cy), 0, &grid_default_cell, "Window value (from window %u): %s%s%s", fs.wl->idx, @@ -831,7 +832,7 @@ window_customize_draw_option(struct window_customize_modedata *data, if (go != NULL && options_owner(o) != go) { parent = options_get_only(go, name); if (parent != NULL) { - value = options_to_string(parent, -1 , 0); + value = options_to_string(parent, NULL, 0); if (!screen_write_text(ctx, s->cx, sx, sy - (s->cy - cy), 0, &grid_default_cell, "Global value: %s%s%s", value, space, unit)) @@ -896,6 +897,8 @@ static const char* window_customize_help_lines[] = { "#[fg=themelightgrey]" " U #[#{E:tree-mode-border-style},acs]x#[default] Unset tagged %1s", "#[fg=themelightgrey]" + " a #[#{E:tree-mode-border-style},acs]x#[default] Change array key", + "#[fg=themelightgrey]" " f #[#{E:tree-mode-border-style},acs]x#[default] Enter a filter", "#[fg=themelightgrey]" " v #[#{E:tree-mode-border-style},acs]x#[default] Toggle information", @@ -1007,8 +1010,10 @@ window_customize_set_option_callback(struct client *c, void *itemdata, const struct options_table_entry *oe; struct options *oo = item->oo; const char *name = item->name; + const char *array_key = item->array_key; char *cause; - int idx = item->idx; + u_int idx; + char keybuf[32]; if (s == NULL || *s == '\0' || data->dead) return (PROMPT_CLOSE); @@ -1020,13 +1025,15 @@ window_customize_set_option_callback(struct client *c, void *itemdata, oe = options_table_entry(o); if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) { - if (idx == -1) { + if (array_key == NULL) { for (idx = 0; idx < INT_MAX; idx++) { - if (options_array_get(o, idx) == NULL) + if (options_array_getv(o, "%u", idx) == NULL) break; } + xsnprintf(keybuf, sizeof keybuf, "%u", idx); + array_key = keybuf; } - if (options_array_set(o, idx, s, 0, &cause) != 0) + if (options_array_set(o, array_key, s, 0, &cause) != 0) goto fail; } else { if (options_from_string(oo, oe, name, s, 0, &cause) != 0) @@ -1056,10 +1063,11 @@ window_customize_set_option(struct client *c, const struct options_table_entry *oe; struct options *oo; struct window_customize_itemdata *new_item; - int flag, idx = item->idx; + int flag; enum window_customize_scope scope = WINDOW_CUSTOMIZE_NONE; u_int choice; const char *name = item->name, *space = ""; + const char *array_key = item->array_key; char *prompt, *value, *text; struct cmd_find_state fs; @@ -1142,25 +1150,26 @@ window_customize_set_option(struct client *c, else if (scope != WINDOW_CUSTOMIZE_SERVER) space = ", global"; if (oe != NULL && (oe->flags & OPTIONS_TABLE_IS_ARRAY)) { - if (idx == -1) { + if (array_key == NULL) { xasprintf(&prompt, "(%s[+]%s%s) ", name, space, text); } else { - xasprintf(&prompt, "(%s[%d]%s%s) ", name, idx, - space, text); + xasprintf(&prompt, "(%s[%s]%s%s) ", name, + array_key, space, text); } } else xasprintf(&prompt, "(%s%s%s) ", name, space, text); free(text); - value = options_to_string(o, idx, 0); + value = options_to_string(o, array_key, 0); new_item = xcalloc(1, sizeof *new_item); new_item->data = data; new_item->scope = scope; new_item->oo = oo; new_item->name = xstrdup(name); - new_item->idx = idx; + if (array_key != NULL) + new_item->array_key = xstrdup(array_key); data->references++; mode_tree_set_prompt(data->data, c, prompt, value, @@ -1173,6 +1182,84 @@ window_customize_set_option(struct client *c, } } +static enum prompt_result +window_customize_set_array_key_callback(struct client *c, void *itemdata, + const char *s, __unused enum prompt_key_result key) +{ + struct window_customize_itemdata *item = itemdata; + struct window_customize_modedata *data; + struct options_entry *o; + const char *name, *array_key; + char *value, *cause; + + if (item == NULL) + return (PROMPT_CLOSE); + data = item->data; + if (s == NULL || *s == '\0' || data->dead) + return (PROMPT_CLOSE); + name = item->name; + array_key = item->array_key; + if (array_key == NULL || !window_customize_check_item(data, item, NULL)) + return (PROMPT_CLOSE); + + o = options_get(item->oo, name); + if (o == NULL) + return (PROMPT_CLOSE); + if (options_array_get(o, s) != NULL) + return (PROMPT_CLOSE); + + value = options_to_string(o, array_key, 0); + if (options_array_set(o, s, value, 0, &cause) != 0) + goto fail; + free(value); + + options_array_set(o, array_key, NULL, 0, NULL); + options_push_changes(item->name); + mode_tree_build(data->data); + mode_tree_draw(data->data); + data->wp->flags |= PANE_REDRAW; + + return (PROMPT_CLOSE); + +fail: + free(value); + *cause = toupper((u_char)*cause); + status_message_set(c, -1, 1, 0, 0, "%s", cause); + free(cause); + return (PROMPT_CLOSE); +} + +static void +window_customize_set_array_key(struct client *c, + struct window_customize_modedata *data, + struct window_customize_itemdata *item) +{ + struct window_customize_itemdata *new_item; + char *prompt; + + if (item == NULL || + item->array_key == NULL || + !window_customize_check_item(data, item, NULL)) + return; + + xasprintf(&prompt, "(%s[%s]) ", item->name, item->array_key); + + new_item = xcalloc(1, sizeof *new_item); + new_item->data = data; + new_item->scope = item->scope; + new_item->oo = item->oo; + new_item->name = xstrdup(item->name); + new_item->array_key = xstrdup(item->array_key); + + data->references++; + mode_tree_set_prompt(data->data, c, prompt, item->array_key, + PROMPT_TYPE_COMMAND, PROMPT_NOFORMAT, + window_customize_set_array_key_callback, + window_customize_free_item_callback, new_item); + + free(prompt); +} + static void window_customize_unset_option(struct window_customize_modedata *data, struct window_customize_itemdata *item) @@ -1185,9 +1272,10 @@ window_customize_unset_option(struct window_customize_modedata *data, o = options_get(item->oo, item->name); if (o == NULL) return; - if (item->idx != -1 && item == mode_tree_get_current(data->data)) + if (item->array_key != NULL && + item == mode_tree_get_current(data->data)) mode_tree_up(data->data, 0); - options_remove_or_default(o, item->idx, NULL); + options_remove_or_default(o, item->array_key, NULL); } static void @@ -1199,14 +1287,14 @@ window_customize_reset_option(struct window_customize_modedata *data, if (item == NULL || !window_customize_check_item(data, item, NULL)) return; - if (item->idx != -1) + if (item->array_key != NULL) return; oo = item->oo; while (oo != NULL) { o = options_get_only(item->oo, item->name); if (o != NULL) - options_remove_or_default(o, -1, NULL); + options_remove_or_default(o, NULL, NULL); oo = options_get_parent(oo); } } @@ -1452,7 +1540,7 @@ window_customize_key(struct window_mode_entry *wme, struct client *c, struct window_pane *wp = wme->wp; struct window_customize_modedata *data = wme->data; struct window_customize_itemdata *item, *new_item; - int finished, idx; + int finished; char *prompt; u_int tagged; @@ -1462,6 +1550,11 @@ window_customize_key(struct window_mode_entry *wme, struct client *c, item = new_item; switch (key) { + case 'a': + if (item == NULL || item->scope == WINDOW_CUSTOMIZE_KEY) + break; + window_customize_set_array_key(c, data, item); + break; case '\r': case 's': if (item == NULL) @@ -1490,7 +1583,7 @@ window_customize_key(struct window_mode_entry *wme, struct client *c, mode_tree_build(data->data); break; case 'd': - if (item == NULL || item->idx != -1) + if (item == NULL || item->array_key != NULL) break; xasprintf(&prompt, "Reset %s to default? ", item->name); data->references++; @@ -1519,10 +1612,10 @@ window_customize_key(struct window_mode_entry *wme, struct client *c, case 'u': if (item == NULL) break; - idx = item->idx; - if (idx != -1) - xasprintf(&prompt, "Unset %s[%d]? ", item->name, idx); - else + if (item->array_key != NULL) { + xasprintf(&prompt, "Unset %s[%s]? ", item->name, + item->array_key); + } else xasprintf(&prompt, "Unset %s? ", item->name); data->references++; data->change = WINDOW_CUSTOMIZE_UNSET;