diff options
Diffstat (limited to 'options.c')
-rw-r--r-- | options.c | 1193 |
1 files changed, 1193 insertions, 0 deletions
diff --git a/options.c b/options.c new file mode 100644 index 0000000..f9cf2af --- /dev/null +++ b/options.c @@ -0,0 +1,1193 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> + +#include <ctype.h> +#include <fnmatch.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> + +#include "tmux.h" + +/* + * Option handling; each option has a name, type and value and is stored in + * a red-black tree. + */ + +struct options_array_item { + u_int index; + union options_value value; + RB_ENTRY(options_array_item) entry; +}; +static int +options_array_cmp(struct options_array_item *a1, struct options_array_item *a2) +{ + if (a1->index < a2->index) + return (-1); + if (a1->index > a2->index) + return (1); + return (0); +} +RB_GENERATE_STATIC(options_array, options_array_item, entry, options_array_cmp); + +struct options_entry { + struct options *owner; + + const char *name; + const struct options_table_entry *tableentry; + union options_value value; + + int cached; + struct style style; + + RB_ENTRY(options_entry) entry; +}; + +struct options { + RB_HEAD(options_tree, options_entry) tree; + struct options *parent; +}; + +static struct options_entry *options_add(struct options *, const char *); +static void options_remove(struct options_entry *); + +#define OPTIONS_IS_STRING(o) \ + ((o)->tableentry == NULL || \ + (o)->tableentry->type == OPTIONS_TABLE_STRING) +#define OPTIONS_IS_NUMBER(o) \ + ((o)->tableentry != NULL && \ + ((o)->tableentry->type == OPTIONS_TABLE_NUMBER || \ + (o)->tableentry->type == OPTIONS_TABLE_KEY || \ + (o)->tableentry->type == OPTIONS_TABLE_COLOUR || \ + (o)->tableentry->type == OPTIONS_TABLE_FLAG || \ + (o)->tableentry->type == OPTIONS_TABLE_CHOICE)) +#define OPTIONS_IS_COMMAND(o) \ + ((o)->tableentry != NULL && \ + (o)->tableentry->type == OPTIONS_TABLE_COMMAND) + +#define OPTIONS_IS_ARRAY(o) \ + ((o)->tableentry != NULL && \ + ((o)->tableentry->flags & OPTIONS_TABLE_IS_ARRAY)) + +static int options_cmp(struct options_entry *, struct options_entry *); +RB_GENERATE_STATIC(options_tree, options_entry, entry, options_cmp); + +static int +options_cmp(struct options_entry *lhs, struct options_entry *rhs) +{ + return (strcmp(lhs->name, rhs->name)); +} + +static const char * +options_map_name(const char *name) +{ + const struct options_name_map *map; + + for (map = options_other_names; map->from != NULL; map++) { + if (strcmp(map->from, name) == 0) + return (map->to); + } + return (name); +} + +static const struct options_table_entry * +options_parent_table_entry(struct options *oo, const char *s) +{ + struct options_entry *o; + + if (oo->parent == NULL) + fatalx("no parent options for %s", s); + o = options_get(oo->parent, s); + if (o == NULL) + fatalx("%s not in parent options", s); + return (o->tableentry); +} + +static void +options_value_free(struct options_entry *o, union options_value *ov) +{ + if (OPTIONS_IS_STRING(o)) + free(ov->string); + if (OPTIONS_IS_COMMAND(o) && ov->cmdlist != NULL) + cmd_list_free(ov->cmdlist); +} + +static char * +options_value_to_string(struct options_entry *o, union options_value *ov, + int numeric) +{ + char *s; + + if (OPTIONS_IS_COMMAND(o)) + return (cmd_list_print(ov->cmdlist, 0)); + if (OPTIONS_IS_NUMBER(o)) { + switch (o->tableentry->type) { + case OPTIONS_TABLE_NUMBER: + xasprintf(&s, "%lld", ov->number); + break; + case OPTIONS_TABLE_KEY: + s = xstrdup(key_string_lookup_key(ov->number, 0)); + break; + case OPTIONS_TABLE_COLOUR: + s = xstrdup(colour_tostring(ov->number)); + break; + case OPTIONS_TABLE_FLAG: + if (numeric) + xasprintf(&s, "%lld", ov->number); + else + s = xstrdup(ov->number ? "on" : "off"); + break; + case OPTIONS_TABLE_CHOICE: + s = xstrdup(o->tableentry->choices[ov->number]); + break; + default: + fatalx("not a number option type"); + } + return (s); + } + if (OPTIONS_IS_STRING(o)) + return (xstrdup(ov->string)); + return (xstrdup("")); +} + +struct options * +options_create(struct options *parent) +{ + struct options *oo; + + oo = xcalloc(1, sizeof *oo); + RB_INIT(&oo->tree); + oo->parent = parent; + return (oo); +} + +void +options_free(struct options *oo) +{ + struct options_entry *o, *tmp; + + RB_FOREACH_SAFE(o, options_tree, &oo->tree, tmp) + options_remove(o); + free(oo); +} + +struct options * +options_get_parent(struct options *oo) +{ + return (oo->parent); +} + +void +options_set_parent(struct options *oo, struct options *parent) +{ + oo->parent = parent; +} + +struct options_entry * +options_first(struct options *oo) +{ + return (RB_MIN(options_tree, &oo->tree)); +} + +struct options_entry * +options_next(struct options_entry *o) +{ + return (RB_NEXT(options_tree, &oo->tree, o)); +} + +struct options_entry * +options_get_only(struct options *oo, const char *name) +{ + struct options_entry o = { .name = name }, *found; + + found = RB_FIND(options_tree, &oo->tree, &o); + if (found == NULL) { + o.name = options_map_name(name); + return (RB_FIND(options_tree, &oo->tree, &o)); + } + return (found); +} + +struct options_entry * +options_get(struct options *oo, const char *name) +{ + struct options_entry *o; + + o = options_get_only(oo, name); + while (o == NULL) { + oo = oo->parent; + if (oo == NULL) + break; + o = options_get_only(oo, name); + } + return (o); +} + +struct options_entry * +options_empty(struct options *oo, const struct options_table_entry *oe) +{ + struct options_entry *o; + + o = options_add(oo, oe->name); + o->tableentry = oe; + + if (oe->flags & OPTIONS_TABLE_IS_ARRAY) + RB_INIT(&o->value.array); + + return (o); +} + +struct options_entry * +options_default(struct options *oo, const struct options_table_entry *oe) +{ + struct options_entry *o; + union options_value *ov; + u_int i; + + o = options_empty(oo, oe); + ov = &o->value; + + if (oe->flags & OPTIONS_TABLE_IS_ARRAY) { + if (oe->default_arr == NULL) { + 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); + return (o); + } + + switch (oe->type) { + case OPTIONS_TABLE_STRING: + ov->string = xstrdup(oe->default_str); + break; + default: + ov->number = oe->default_num; + break; + } + return (o); +} + +char * +options_default_to_string(const struct options_table_entry *oe) +{ + char *s; + + switch (oe->type) { + case OPTIONS_TABLE_STRING: + case OPTIONS_TABLE_COMMAND: + s = xstrdup(oe->default_str); + break; + case OPTIONS_TABLE_NUMBER: + xasprintf(&s, "%lld", oe->default_num); + break; + case OPTIONS_TABLE_KEY: + s = xstrdup(key_string_lookup_key(oe->default_num, 0)); + break; + case OPTIONS_TABLE_COLOUR: + s = xstrdup(colour_tostring(oe->default_num)); + break; + case OPTIONS_TABLE_FLAG: + s = xstrdup(oe->default_num ? "on" : "off"); + break; + case OPTIONS_TABLE_CHOICE: + s = xstrdup(oe->choices[oe->default_num]); + break; + default: + fatalx("unknown option type"); + } + return (s); +} + +static struct options_entry * +options_add(struct options *oo, const char *name) +{ + struct options_entry *o; + + o = options_get_only(oo, name); + if (o != NULL) + options_remove(o); + + o = xcalloc(1, sizeof *o); + o->owner = oo; + o->name = xstrdup(name); + + RB_INSERT(options_tree, &oo->tree, o); + return (o); +} + +static void +options_remove(struct options_entry *o) +{ + struct options *oo = o->owner; + + if (OPTIONS_IS_ARRAY(o)) + options_array_clear(o); + else + options_value_free(o, &o->value); + RB_REMOVE(options_tree, &oo->tree, o); + free((void *)o->name); + free(o); +} + +const char * +options_name(struct options_entry *o) +{ + return (o->name); +} + +struct options * +options_owner(struct options_entry *o) +{ + return (o->owner); +} + +const struct options_table_entry * +options_table_entry(struct options_entry *o) +{ + return (o->tableentry); +} + +static struct options_array_item * +options_array_item(struct options_entry *o, u_int idx) +{ + struct options_array_item a; + + a.index = idx; + return (RB_FIND(options_array, &o->value.array, &a)); +} + +static struct options_array_item * +options_array_new(struct options_entry *o, u_int idx) +{ + struct options_array_item *a; + + a = xcalloc(1, sizeof *a); + a->index = idx; + RB_INSERT(options_array, &o->value.array, a); + return (a); +} + +static void +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); +} + +void +options_array_clear(struct options_entry *o) +{ + struct options_array_item *a, *a1; + + if (!OPTIONS_IS_ARRAY(o)) + return; + + RB_FOREACH_SAFE(a, options_array, &o->value.array, a1) + options_array_free(o, a); +} + +union options_value * +options_array_get(struct options_entry *o, u_int idx) +{ + struct options_array_item *a; + + if (!OPTIONS_IS_ARRAY(o)) + return (NULL); + a = options_array_item(o, idx); + if (a == NULL) + return (NULL); + return (&a->value); +} + +int +options_array_set(struct options_entry *o, u_int idx, const char *value, + int append, char **cause) +{ + struct options_array_item *a; + char *new; + struct cmd_parse_result *pr; + long long number; + + if (!OPTIONS_IS_ARRAY(o)) { + if (cause != NULL) + *cause = xstrdup("not an array"); + return (-1); + } + + if (value == NULL) { + a = options_array_item(o, idx); + if (a != NULL) + options_array_free(o, a); + return (0); + } + + if (OPTIONS_IS_COMMAND(o)) { + pr = cmd_parse_from_string(value, NULL); + switch (pr->status) { + case CMD_PARSE_ERROR: + if (cause != NULL) + *cause = pr->error; + else + free(pr->error); + return (-1); + case CMD_PARSE_SUCCESS: + break; + } + + a = options_array_item(o, idx); + if (a == NULL) + a = options_array_new(o, idx); + else + options_value_free(o, &a->value); + a->value.cmdlist = pr->cmdlist; + return (0); + } + + if (OPTIONS_IS_STRING(o)) { + a = options_array_item(o, idx); + 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); + else + options_value_free(o, &a->value); + a->value.string = new; + return (0); + } + + if (o->tableentry->type == OPTIONS_TABLE_COLOUR) { + if ((number = colour_fromstring(value)) == -1) { + xasprintf(cause, "bad colour: %s", value); + return (-1); + } + a = options_array_item(o, idx); + if (a == NULL) + a = options_array_new(o, idx); + else + options_value_free(o, &a->value); + a->value.number = number; + return (0); + } + + if (cause != NULL) + *cause = xstrdup("wrong array type"); + return (-1); +} + +int +options_array_assign(struct options_entry *o, const char *s, char **cause) +{ + const char *separator; + char *copy, *next, *string; + u_int i; + + separator = o->tableentry->separator; + if (separator == NULL) + separator = " ,"; + if (*separator == '\0') { + if (*s == '\0') + return (0); + for (i = 0; i < UINT_MAX; i++) { + if (options_array_item(o, i) == NULL) + break; + } + return (options_array_set(o, i, s, 0, cause)); + } + + if (*s == '\0') + return (0); + copy = string = xstrdup(s); + while ((next = strsep(&string, separator)) != NULL) { + if (*next == '\0') + continue; + for (i = 0; i < UINT_MAX; i++) { + if (options_array_item(o, i) == NULL) + break; + } + if (i == UINT_MAX) + break; + if (options_array_set(o, i, next, 0, cause) != 0) { + free(copy); + return (-1); + } + } + free(copy); + return (0); +} + +struct options_array_item * +options_array_first(struct options_entry *o) +{ + if (!OPTIONS_IS_ARRAY(o)) + return (NULL); + return (RB_MIN(options_array, &o->value.array)); +} + +struct options_array_item * +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) +{ + return (a->index); +} + +union options_value * +options_array_item_value(struct options_array_item *a) +{ + return (&a->value); +} + +int +options_is_array(struct options_entry *o) +{ + return (OPTIONS_IS_ARRAY(o)); +} + +int +options_is_string(struct options_entry *o) +{ + return (OPTIONS_IS_STRING(o)); +} + +char * +options_to_string(struct options_entry *o, int idx, int numeric) +{ + struct options_array_item *a; + + if (OPTIONS_IS_ARRAY(o)) { + if (idx == -1) + return (xstrdup("")); + a = options_array_item(o, idx); + if (a == NULL) + return (xstrdup("")); + return (options_value_to_string(o, &a->value, numeric)); + } + return (options_value_to_string(o, &o->value, numeric)); +} + +char * +options_parse(const char *name, int *idx) +{ + char *copy, *cp, *end; + + if (*name == '\0') + return (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])) { + free(copy); + return (NULL); + } + if (sscanf(cp, "[%d]", idx) != 1 || *idx < 0) { + free(copy); + return (NULL); + } + *cp = '\0'; + return (copy); +} + +struct options_entry * +options_parse_get(struct options *oo, const char *s, int *idx, int only) +{ + struct options_entry *o; + char *name; + + name = options_parse(s, idx); + if (name == NULL) + return (NULL); + if (only) + o = options_get_only(oo, name); + else + o = options_get(oo, name); + free(name); + return (o); +} + +char * +options_match(const char *s, int *idx, int *ambiguous) +{ + const struct options_table_entry *oe, *found; + char *parsed; + const char *name; + size_t namelen; + + parsed = options_parse(s, idx); + if (parsed == NULL) + return (NULL); + if (*parsed == '@') { + *ambiguous = 0; + return (parsed); + } + + name = options_map_name(parsed); + namelen = strlen(name); + + found = NULL; + for (oe = options_table; oe->name != NULL; oe++) { + if (strcmp(oe->name, name) == 0) { + found = oe; + break; + } + if (strncmp(oe->name, name, namelen) == 0) { + if (found != NULL) { + *ambiguous = 1; + free(parsed); + return (NULL); + } + found = oe; + } + } + free(parsed); + if (found == NULL) { + *ambiguous = 0; + return (NULL); + } + return (xstrdup(found->name)); +} + +struct options_entry * +options_match_get(struct options *oo, const char *s, int *idx, int only, + int *ambiguous) +{ + char *name; + struct options_entry *o; + + name = options_match(s, idx, ambiguous); + if (name == NULL) + return (NULL); + *ambiguous = 0; + if (only) + o = options_get_only(oo, name); + else + o = options_get(oo, name); + free(name); + return (o); +} + +const char * +options_get_string(struct options *oo, const char *name) +{ + struct options_entry *o; + + o = options_get(oo, name); + if (o == NULL) + fatalx("missing option %s", name); + if (!OPTIONS_IS_STRING(o)) + fatalx("option %s is not a string", name); + return (o->value.string); +} + +long long +options_get_number(struct options *oo, const char *name) +{ + struct options_entry *o; + + o = options_get(oo, name); + if (o == NULL) + fatalx("missing option %s", name); + if (!OPTIONS_IS_NUMBER(o)) + fatalx("option %s is not a number", name); + return (o->value.number); +} + +struct options_entry * +options_set_string(struct options *oo, const char *name, int append, + const char *fmt, ...) +{ + struct options_entry *o; + va_list ap; + const char *separator = ""; + char *s, *value; + + va_start(ap, fmt); + xvasprintf(&s, fmt, ap); + va_end(ap); + + o = options_get_only(oo, name); + if (o != NULL && append && OPTIONS_IS_STRING(o)) { + if (*name != '@') { + separator = o->tableentry->separator; + if (separator == NULL) + separator = ""; + } + xasprintf(&value, "%s%s%s", o->value.string, separator, s); + free(s); + } else + value = s; + if (o == NULL && *name == '@') + o = options_add(oo, name); + else if (o == NULL) { + o = options_default(oo, options_parent_table_entry(oo, name)); + if (o == NULL) + return (NULL); + } + + if (!OPTIONS_IS_STRING(o)) + fatalx("option %s is not a string", name); + free(o->value.string); + o->value.string = value; + o->cached = 0; + return (o); +} + +struct options_entry * +options_set_number(struct options *oo, const char *name, long long value) +{ + struct options_entry *o; + + if (*name == '@') + fatalx("user option %s must be a string", name); + + o = options_get_only(oo, name); + if (o == NULL) { + o = options_default(oo, options_parent_table_entry(oo, name)); + if (o == NULL) + return (NULL); + } + + if (!OPTIONS_IS_NUMBER(o)) + fatalx("option %s is not a number", name); + o->value.number = value; + return (o); +} + +int +options_scope_from_name(struct args *args, int window, + const char *name, struct cmd_find_state *fs, struct options **oo, + char **cause) +{ + struct session *s = fs->s; + struct winlink *wl = fs->wl; + struct window_pane *wp = fs->wp; + const char *target = args_get(args, 't'); + const struct options_table_entry *oe; + int scope = OPTIONS_TABLE_NONE; + + if (*name == '@') + return (options_scope_from_flags(args, window, fs, oo, cause)); + + for (oe = options_table; oe->name != NULL; oe++) { + if (strcmp(oe->name, name) == 0) + break; + } + if (oe->name == NULL) { + xasprintf(cause, "unknown option: %s", name); + return (OPTIONS_TABLE_NONE); + } + switch (oe->scope) { + case OPTIONS_TABLE_SERVER: + *oo = global_options; + scope = OPTIONS_TABLE_SERVER; + break; + case OPTIONS_TABLE_SESSION: + if (args_has(args, 'g')) { + *oo = global_s_options; + scope = OPTIONS_TABLE_SESSION; + } else if (s == NULL && target != NULL) + xasprintf(cause, "no such session: %s", target); + else if (s == NULL) + xasprintf(cause, "no current session"); + else { + *oo = s->options; + scope = OPTIONS_TABLE_SESSION; + } + break; + case OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE: + if (args_has(args, 'p')) { + if (wp == NULL && target != NULL) + xasprintf(cause, "no such pane: %s", target); + else if (wp == NULL) + xasprintf(cause, "no current pane"); + else { + *oo = wp->options; + scope = OPTIONS_TABLE_PANE; + } + break; + } + /* FALLTHROUGH */ + case OPTIONS_TABLE_WINDOW: + if (args_has(args, 'g')) { + *oo = global_w_options; + scope = OPTIONS_TABLE_WINDOW; + } else if (wl == NULL && target != NULL) + xasprintf(cause, "no such window: %s", target); + else if (wl == NULL) + xasprintf(cause, "no current window"); + else { + *oo = wl->window->options; + scope = OPTIONS_TABLE_WINDOW; + } + break; + } + return (scope); +} + +int +options_scope_from_flags(struct args *args, int window, + struct cmd_find_state *fs, struct options **oo, char **cause) +{ + struct session *s = fs->s; + struct winlink *wl = fs->wl; + struct window_pane *wp = fs->wp; + const char *target = args_get(args, 't'); + + if (args_has(args, 's')) { + *oo = global_options; + return (OPTIONS_TABLE_SERVER); + } + + if (args_has(args, 'p')) { + if (wp == NULL) { + if (target != NULL) + xasprintf(cause, "no such pane: %s", target); + else + xasprintf(cause, "no current pane"); + return (OPTIONS_TABLE_NONE); + } + *oo = wp->options; + return (OPTIONS_TABLE_PANE); + } else if (window || args_has(args, 'w')) { + if (args_has(args, 'g')) { + *oo = global_w_options; + return (OPTIONS_TABLE_WINDOW); + } + if (wl == NULL) { + if (target != NULL) + xasprintf(cause, "no such window: %s", target); + else + xasprintf(cause, "no current window"); + return (OPTIONS_TABLE_NONE); + } + *oo = wl->window->options; + return (OPTIONS_TABLE_WINDOW); + } else { + if (args_has(args, 'g')) { + *oo = global_s_options; + return (OPTIONS_TABLE_SESSION); + } + if (s == NULL) { + if (target != NULL) + xasprintf(cause, "no such session: %s", target); + else + xasprintf(cause, "no current session"); + return (OPTIONS_TABLE_NONE); + } + *oo = s->options; + return (OPTIONS_TABLE_SESSION); + } +} + +struct style * +options_string_to_style(struct options *oo, const char *name, + struct format_tree *ft) +{ + struct options_entry *o; + const char *s; + char *expanded; + + o = options_get(oo, name); + if (o == NULL || !OPTIONS_IS_STRING(o)) + return (NULL); + + if (o->cached) + return (&o->style); + s = o->value.string; + log_debug("%s: %s is '%s'", __func__, name, s); + + style_set(&o->style, &grid_default_cell); + o->cached = (strstr(s, "#{") == NULL); + + if (ft != NULL && !o->cached) { + expanded = format_expand(ft, s); + if (style_parse(&o->style, &grid_default_cell, expanded) != 0) { + free(expanded); + return (NULL); + } + free(expanded); + } else { + if (style_parse(&o->style, &grid_default_cell, s) != 0) + return (NULL); + } + return (&o->style); +} + +static int +options_from_string_check(const struct options_table_entry *oe, + const char *value, char **cause) +{ + struct style sy; + + if (oe == NULL) + return (0); + if (strcmp(oe->name, "default-shell") == 0 && !checkshell(value)) { + xasprintf(cause, "not a suitable shell: %s", value); + return (-1); + } + if (oe->pattern != NULL && fnmatch(oe->pattern, value, 0) != 0) { + xasprintf(cause, "value is invalid: %s", value); + return (-1); + } + if ((oe->flags & OPTIONS_TABLE_IS_STYLE) && + strstr(value, "#{") == NULL && + style_parse(&sy, &grid_default_cell, value) != 0) { + xasprintf(cause, "invalid style: %s", value); + return (-1); + } + return (0); +} + +static int +options_from_string_flag(struct options *oo, const char *name, + const char *value, char **cause) +{ + int flag; + + if (value == NULL || *value == '\0') + flag = !options_get_number(oo, name); + else if (strcmp(value, "1") == 0 || + strcasecmp(value, "on") == 0 || + strcasecmp(value, "yes") == 0) + flag = 1; + else if (strcmp(value, "0") == 0 || + strcasecmp(value, "off") == 0 || + strcasecmp(value, "no") == 0) + flag = 0; + else { + xasprintf(cause, "bad value: %s", value); + return (-1); + } + options_set_number(oo, name, flag); + return (0); +} + +int +options_find_choice(const struct options_table_entry *oe, const char *value, + char **cause) +{ + const char **cp; + int n = 0, choice = -1; + + for (cp = oe->choices; *cp != NULL; cp++) { + if (strcmp(*cp, value) == 0) + choice = n; + n++; + } + if (choice == -1) { + xasprintf(cause, "unknown value: %s", value); + return (-1); + } + return (choice); +} + +static int +options_from_string_choice(const struct options_table_entry *oe, + struct options *oo, const char *name, const char *value, char **cause) +{ + int choice = -1; + + if (value == NULL) { + choice = options_get_number(oo, name); + if (choice < 2) + choice = !choice; + } else { + choice = options_find_choice(oe, value, cause); + if (choice < 0) + return (-1); + } + options_set_number(oo, name, choice); + return (0); +} + +int +options_from_string(struct options *oo, const struct options_table_entry *oe, + const char *name, const char *value, int append, char **cause) +{ + enum options_table_type type; + long long number; + const char *errstr, *new; + char *old; + key_code key; + + if (oe != NULL) { + if (value == NULL && + oe->type != OPTIONS_TABLE_FLAG && + oe->type != OPTIONS_TABLE_CHOICE) { + xasprintf(cause, "empty value"); + return (-1); + } + type = oe->type; + } else { + if (*name != '@') { + xasprintf(cause, "bad option name"); + return (-1); + } + type = OPTIONS_TABLE_STRING; + } + + switch (type) { + case OPTIONS_TABLE_STRING: + old = xstrdup(options_get_string(oo, name)); + options_set_string(oo, name, append, "%s", value); + + new = options_get_string(oo, name); + if (options_from_string_check(oe, new, cause) != 0) { + options_set_string(oo, name, 0, "%s", old); + free(old); + return (-1); + } + free(old); + return (0); + case OPTIONS_TABLE_NUMBER: + number = strtonum(value, oe->minimum, oe->maximum, &errstr); + if (errstr != NULL) { + xasprintf(cause, "value is %s: %s", errstr, value); + return (-1); + } + options_set_number(oo, name, number); + return (0); + case OPTIONS_TABLE_KEY: + key = key_string_lookup_string(value); + if (key == KEYC_UNKNOWN) { + xasprintf(cause, "bad key: %s", value); + return (-1); + } + options_set_number(oo, name, key); + return (0); + case OPTIONS_TABLE_COLOUR: + if ((number = colour_fromstring(value)) == -1) { + xasprintf(cause, "bad colour: %s", value); + return (-1); + } + options_set_number(oo, name, number); + return (0); + case OPTIONS_TABLE_FLAG: + return (options_from_string_flag(oo, name, value, cause)); + case OPTIONS_TABLE_CHOICE: + return (options_from_string_choice(oe, oo, name, value, cause)); + case OPTIONS_TABLE_COMMAND: + break; + } + return (-1); +} + +void +options_push_changes(const char *name) +{ + struct client *loop; + struct session *s; + struct window *w; + struct window_pane *wp; + int c; + + log_debug("%s: %s", __func__, name); + + if (strcmp(name, "automatic-rename") == 0) { + RB_FOREACH(w, windows, &windows) { + if (w->active == NULL) + continue; + if (options_get_number(w->options, name)) + w->active->flags |= PANE_CHANGED; + } + } + if (strcmp(name, "cursor-colour") == 0) { + RB_FOREACH(wp, window_pane_tree, &all_window_panes) { + c = options_get_number(wp->options, name); + wp->screen->default_ccolour = c; + } + } + if (strcmp(name, "cursor-style") == 0) { + RB_FOREACH(wp, window_pane_tree, &all_window_panes) { + wp->screen->default_mode = 0; + screen_set_cursor_style(options_get_number(wp->options, + name), &wp->screen->default_cstyle, + &wp->screen->default_mode); + } + } + if (strcmp(name, "fill-character") == 0) { + RB_FOREACH(w, windows, &windows) + window_set_fill_character(w); + } + if (strcmp(name, "key-table") == 0) { + TAILQ_FOREACH(loop, &clients, entry) + server_client_set_key_table(loop, NULL); + } + if (strcmp(name, "user-keys") == 0) { + TAILQ_FOREACH(loop, &clients, entry) { + if (loop->tty.flags & TTY_OPENED) + tty_keys_build(&loop->tty); + } + } + if (strcmp(name, "status") == 0 || + strcmp(name, "status-interval") == 0) + status_timer_start_all(); + if (strcmp(name, "monitor-silence") == 0) + alerts_reset_all(); + if (strcmp(name, "window-style") == 0 || + strcmp(name, "window-active-style") == 0) { + RB_FOREACH(wp, window_pane_tree, &all_window_panes) + wp->flags |= PANE_STYLECHANGED; + } + if (strcmp(name, "pane-colours") == 0) { + RB_FOREACH(wp, window_pane_tree, &all_window_panes) + colour_palette_from_option(&wp->palette, wp->options); + } + if (strcmp(name, "pane-border-status") == 0) { + RB_FOREACH(w, windows, &windows) + layout_fix_panes(w, NULL); + } + RB_FOREACH(s, sessions, &sessions) + status_update_cache(s); + + recalculate_sizes(); + TAILQ_FOREACH(loop, &clients, entry) { + if (loop->session != NULL) + server_redraw_client(loop); + } +} + +int +options_remove_or_default(struct options_entry *o, int idx, char **cause) +{ + struct options *oo = o->owner; + + if (idx == -1) { + if (o->tableentry != NULL && + (oo == global_options || + oo == global_s_options || + oo == global_w_options)) + options_default(oo, o->tableentry); + else + options_remove(o); + } else if (options_array_set(o, idx, NULL, 0, cause) != 0) + return (-1); + return (0); +} |