diff --git a/cmd-set-option.c b/cmd-set-option.c index ab15eb70..475b91c2 100644 --- a/cmd-set-option.c +++ b/cmd-set-option.c @@ -29,40 +29,14 @@ static enum cmd_retval cmd_set_option_exec(struct cmd *, struct cmdq_item *); -static enum cmd_retval cmd_set_option_user(struct cmd *, struct cmdq_item *, - const char *, const char *); - -static int cmd_set_option_unset(struct cmd *, struct cmdq_item *, - const struct options_table_entry *, struct options *, - const char *); static int cmd_set_option_set(struct cmd *, struct cmdq_item *, + struct options *, struct option *, const char *); +static int cmd_set_option_flag(struct cmdq_item *, + const struct options_table_entry *, struct options *, + const char *); +static int cmd_set_option_choice(struct cmdq_item *, const struct options_table_entry *, struct options *, const char *); - -static struct options_entry *cmd_set_option_string(struct cmd *, - struct cmdq_item *, const struct options_table_entry *, - struct options *, const char *); -static struct options_entry *cmd_set_option_number(struct cmd *, - struct cmdq_item *, const struct options_table_entry *, - struct options *, const char *); -static struct options_entry *cmd_set_option_key(struct cmd *, - struct cmdq_item *, const struct options_table_entry *, - struct options *, const char *); -static struct options_entry *cmd_set_option_colour(struct cmd *, - struct cmdq_item *, const struct options_table_entry *, - struct options *, const char *); -static struct options_entry *cmd_set_option_attributes(struct cmd *, - struct cmdq_item *, const struct options_table_entry *, - struct options *, const char *); -static struct options_entry *cmd_set_option_flag(struct cmd *, - struct cmdq_item *, const struct options_table_entry *, - struct options *, const char *); -static struct options_entry *cmd_set_option_choice(struct cmd *, - struct cmdq_item *, const struct options_table_entry *, - struct options *, const char *); -static struct options_entry *cmd_set_option_style(struct cmd *, - struct cmdq_item *, const struct options_table_entry *, - struct options *, const char *); const struct cmd_entry cmd_set_option_entry = { .name = "set-option", @@ -93,99 +67,154 @@ const struct cmd_entry cmd_set_window_option_entry = { static enum cmd_retval cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = self->args; - struct session *s = item->state.tflag.s; - struct winlink *wl = item->state.tflag.wl; - struct window *w; - struct client *c; - const struct options_table_entry *oe; - struct options *oo; - const char *optstr, *valstr, *target; + struct args *args = self->args; + struct cmd_find_state *fs = &item->state.tflag; + struct session *s = fs->s; + struct winlink *wl = fs->wl; + struct window *w = wl->window; + struct client *c; + enum options_table_scope scope; + struct options *oo; + struct option *parent, *o; + const char *name, *value, *target; + int window, idx, already, error, ambiguous; + char *cause; - /* Get the option name and value. */ - optstr = args->argv[0]; - if (*optstr == '\0') { - cmdq_error(item, "invalid option"); + /* Parse option name and index. */ + name = options_match(args->argv[0], &idx, &ambiguous); + if (name == NULL) { + if (args_has(args, 'q')) + return (CMD_RETURN_NORMAL); + if (ambiguous) + cmdq_error(item, "ambiguous option: %s", args->argv[0]); + else + cmdq_error(item, "invalid option: %s", args->argv[0]); return (CMD_RETURN_ERROR); } if (args->argc < 2) - valstr = NULL; + value = NULL; else - valstr = args->argv[1]; + value = args->argv[1]; - /* Is this a user option? */ - if (*optstr == '@') - return (cmd_set_option_user(self, item, optstr, valstr)); - - /* Find the option entry. */ - oe = NULL; - if (options_table_find(optstr, &oe) != 0) { - if (!args_has(args, 'q')) { - cmdq_error(item, "ambiguous option: %s", optstr); - return (CMD_RETURN_ERROR); + /* + * Figure out the scope: for user options it comes from the arguments, + * otherwise from the option name. + */ + if (*name == '@') { + window = (self->entry == &cmd_set_window_option_entry); + scope = options_scope_from_flags(args, window, fs, &oo, &cause); + } else { + if (options_get_only(global_options, name) != NULL) + scope = OPTIONS_TABLE_SERVER; + else if (options_get_only(global_s_options, name) != NULL) + scope = OPTIONS_TABLE_SESSION; + else if (options_get_only(global_w_options, name) != NULL) + scope = OPTIONS_TABLE_WINDOW; + else { + scope = OPTIONS_TABLE_NONE; + xasprintf(&cause, "unknown option: %s", args->argv[0]); } - return (CMD_RETURN_NORMAL); } - if (oe == NULL) { - if (!args_has(args, 'q')) { - cmdq_error(item, "unknown option: %s", optstr); - return (CMD_RETURN_ERROR); - } - return (CMD_RETURN_NORMAL); + if (scope == OPTIONS_TABLE_NONE) { + if (args_has(args, 'q')) + return (CMD_RETURN_NORMAL); + cmdq_error(item, "%s", cause); + free(cause); + return (CMD_RETURN_ERROR); } - /* Work out the tree from the scope of the option. */ - if (oe->scope == OPTIONS_TABLE_SERVER) + /* Which table should this option go into? */ + if (scope == OPTIONS_TABLE_SERVER) oo = global_options; - else if (oe->scope == OPTIONS_TABLE_WINDOW) { - if (args_has(self->args, 'g')) - oo = global_w_options; - else if (wl == NULL) { - target = args_get(args, 't'); - if (target != NULL) { - cmdq_error(item, "no such window: %s", - target); - } else - cmdq_error(item, "no current window"); - return (CMD_RETURN_ERROR); - } else - oo = wl->window->options; - } else if (oe->scope == OPTIONS_TABLE_SESSION) { + else if (scope == OPTIONS_TABLE_SESSION) { if (args_has(self->args, 'g')) oo = global_s_options; else if (s == NULL) { target = args_get(args, 't'); - if (target != NULL) { - cmdq_error(item, "no such session: %s", - target); - } else + if (target != NULL) + cmdq_error(item, "no such session: %s", target); + else cmdq_error(item, "no current session"); return (CMD_RETURN_ERROR); } else oo = s->options; + } else if (scope == OPTIONS_TABLE_WINDOW) { + if (args_has(self->args, 'g')) + oo = global_w_options; + else if (wl == NULL) { + target = args_get(args, 't'); + if (target != NULL) + cmdq_error(item, "no such window: %s", target); + else + cmdq_error(item, "no current window"); + return (CMD_RETURN_ERROR); + } else + oo = wl->window->options; + } + o = options_get_only(oo, name); + parent = options_get(oo, name); + + /* Check that array options and indexes match up. */ + if (idx != -1) { + if (*name == '@' || options_array_size(parent, NULL) == -1) { + cmdq_error(item, "not an array: %s", args->argv[0]); + return (CMD_RETURN_ERROR); + } } else { - cmdq_error(item, "unknown table"); - return (CMD_RETURN_ERROR); + if (*name != '@' && options_array_size(parent, NULL) != -1) { + cmdq_error(item, "is an array: %s", args->argv[0]); + return (CMD_RETURN_ERROR); + } } - /* Unset or set the option. */ + /* With -o, check this option is not already set. */ + if (!args_has(args, 'u') && args_has(args, 'o')) { + if (idx == -1) + already = (o != NULL); + else { + if (o == NULL) + already = 0; + else + already = (options_array_get(o, idx) != NULL); + } + if (already) { + if (args_has(args, 'q')) + return (CMD_RETURN_NORMAL); + cmdq_error(item, "already set: %s", args->argv[0]); + return (CMD_RETURN_ERROR); + } + } + + /* Change the option. */ if (args_has(args, 'u')) { - if (cmd_set_option_unset(self, item, oe, oo, valstr) != 0) + if (o == NULL) + return (CMD_RETURN_NORMAL); + if (idx == -1) { + if (oo == global_options || + oo == global_s_options || + oo == global_w_options) + options_default(oo, options_table_entry(o)); + else + options_remove(o); + } else + options_array_set(o, idx, NULL); + } else if (*name == '@') + options_set_string(oo, name, args_has(args, 'a'), "%s", value); + else if (idx == -1) { + error = cmd_set_option_set(self, item, oo, parent, value); + if (error != 0) return (CMD_RETURN_ERROR); } else { - if (args_has(args, 'o') && options_find1(oo, optstr) != NULL) { - if (!args_has(args, 'q')) { - cmdq_error(item, "already set: %s", optstr); - return (CMD_RETURN_ERROR); - } - return (CMD_RETURN_NORMAL); - } - if (cmd_set_option_set(self, item, oe, oo, valstr) != 0) + if (o == NULL) + o = options_empty(oo, options_table_entry(parent)); + if (options_array_set(o, idx, value) != 0) { + cmdq_error(item, "invalid index: %s", args->argv[0]); return (CMD_RETURN_ERROR); + } } /* Update timers and so on for various options. */ - if (strcmp(oe->name, "automatic-rename") == 0) { + if (strcmp(name, "automatic-rename") == 0) { RB_FOREACH(w, windows, &windows) { if (w->active == NULL) continue; @@ -193,26 +222,29 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) w->active->flags |= PANE_CHANGED; } } - if (strcmp(oe->name, "key-table") == 0) { + if (strcmp(name, "key-table") == 0) { TAILQ_FOREACH(c, &clients, entry) server_client_set_key_table(c, NULL); } - if (strcmp(oe->name, "status") == 0 || - strcmp(oe->name, "status-interval") == 0) + if (strcmp(name, "status") == 0 || + strcmp(name, "status-interval") == 0) status_timer_start_all(); - if (strcmp(oe->name, "monitor-silence") == 0) + if (strcmp(name, "monitor-silence") == 0) alerts_reset_all(); - if (strcmp(oe->name, "window-style") == 0 || - strcmp(oe->name, "window-active-style") == 0) { + if (strcmp(name, "window-style") == 0 || + strcmp(name, "window-active-style") == 0) { RB_FOREACH(w, windows, &windows) w->flags |= WINDOW_STYLECHANGED; } - if (strcmp(oe->name, "pane-border-status") == 0) { + if (strcmp(name, "pane-border-status") == 0) { RB_FOREACH(w, windows, &windows) layout_fix_panes(w, w->sx, w->sy); } - /* Update sizes and redraw. May not need it but meh. */ + /* + * Update sizes and redraw. May not always be necessary but do it + * anyway. + */ recalculate_sizes(); TAILQ_FOREACH(c, &clients, entry) { if (c->session != NULL) @@ -222,250 +254,82 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); } -/* Set user option. */ -static enum cmd_retval -cmd_set_option_user(struct cmd *self, struct cmdq_item *item, - const char *optstr, const char *valstr) -{ - struct args *args = self->args; - struct session *s = item->state.tflag.s; - struct winlink *wl = item->state.tflag.wl; - struct options *oo; - struct options_entry *o; - const char *target; - - if (args_has(args, 's')) - oo = global_options; - else if (args_has(self->args, 'w') || - self->entry == &cmd_set_window_option_entry) { - if (args_has(self->args, 'g')) - oo = global_w_options; - else if (wl == NULL) { - target = args_get(args, 't'); - if (target != NULL) { - cmdq_error(item, "no such window: %s", - target); - } else - cmdq_error(item, "no current window"); - return (CMD_RETURN_ERROR); - } else - oo = wl->window->options; - } else { - if (args_has(self->args, 'g')) - oo = global_s_options; - else if (s == NULL) { - target = args_get(args, 't'); - if (target != NULL) { - cmdq_error(item, "no such session: %s", - target); - } else - cmdq_error(item, "no current session"); - return (CMD_RETURN_ERROR); - } else - oo = s->options; - } - - if (args_has(args, 'u')) { - if (options_find1(oo, optstr) == NULL) { - if (!args_has(args, 'q')) { - cmdq_error(item, "unknown option: %s", optstr); - return (CMD_RETURN_ERROR); - } - return (CMD_RETURN_NORMAL); - } - if (valstr != NULL) { - cmdq_error(item, "value passed to unset option: %s", - optstr); - return (CMD_RETURN_ERROR); - } - options_remove(oo, optstr); - } else { - o = options_find1(oo, optstr); - if (args_has(args, 'o') && o != NULL) { - if (!args_has(args, 'q')) { - cmdq_error(item, "already set: %s", optstr); - return (CMD_RETURN_ERROR); - } - return (CMD_RETURN_NORMAL); - } - if (valstr == NULL) { - cmdq_error(item, "empty value"); - return (CMD_RETURN_ERROR); - } - options_set_string(oo, optstr, args_has(args, 'a'), "%s", - valstr); - } - return (CMD_RETURN_NORMAL); -} - -/* Unset an option. */ static int -cmd_set_option_unset(struct cmd *self, struct cmdq_item *item, - const struct options_table_entry *oe, struct options *oo, - const char *value) +cmd_set_option_set(struct cmd *self, struct cmdq_item *item, struct options *oo, + struct option *parent, const char *value) { - struct args *args = self->args; + const struct options_table_entry *oe; + struct args *args = self->args; + int append = args_has(args, 'a'); + struct option *o; + long long number; + const char *errstr; + key_code key; - if (value != NULL) { - cmdq_error(item, "value passed to unset option: %s", oe->name); + oe = options_table_entry(parent); + if (value == NULL && + oe->type != OPTIONS_TABLE_FLAG && + oe->type != OPTIONS_TABLE_CHOICE) { + cmdq_error(item, "empty value"); return (-1); } - if (args_has(args, 'g') || oo == global_options) { - switch (oe->type) { - case OPTIONS_TABLE_STRING: - options_set_string(oo, oe->name, 0, "%s", - oe->default_str); - break; - case OPTIONS_TABLE_STYLE: - options_set_style(oo, oe->name, 0, oe->default_str); - break; - default: - options_set_number(oo, oe->name, oe->default_num); - break; - } - } else - options_remove(oo, oe->name); - return (0); -} - -/* Set an option. */ -static int -cmd_set_option_set(struct cmd *self, struct cmdq_item *item, - const struct options_table_entry *oe, struct options *oo, - const char *value) -{ - struct options_entry *o; - - switch (oe->type) { - case OPTIONS_TABLE_FLAG: - case OPTIONS_TABLE_CHOICE: - break; - default: - if (value == NULL) { - cmdq_error(item, "empty value"); - return (-1); - } - } - - o = NULL; switch (oe->type) { case OPTIONS_TABLE_STRING: - o = cmd_set_option_string(self, item, oe, oo, value); - break; + options_set_string(oo, oe->name, append, "%s", value); + return (0); case OPTIONS_TABLE_NUMBER: - o = cmd_set_option_number(self, item, oe, oo, value); - break; + number = strtonum(value, oe->minimum, oe->maximum, &errstr); + if (errstr != NULL) { + cmdq_error(item, "value is %s: %s", errstr, value); + return (-1); + } + options_set_number(oo, oe->name, number); + return (0); case OPTIONS_TABLE_KEY: - o = cmd_set_option_key(self, item, oe, oo, value); - break; + key = key_string_lookup_string(value); + if (key == KEYC_UNKNOWN) { + cmdq_error(item, "bad key: %s", value); + return (-1); + } + options_set_number(oo, oe->name, key); + return (0); case OPTIONS_TABLE_COLOUR: - o = cmd_set_option_colour(self, item, oe, oo, value); - if (o != NULL) - style_update_new(oo, o->name, oe->style); - break; + if ((number = colour_fromstring(value)) == -1) { + cmdq_error(item, "bad colour: %s", value); + return (-1); + } + o = options_set_number(oo, oe->name, number); + options_style_update_new(oo, o); + return (0); case OPTIONS_TABLE_ATTRIBUTES: - o = cmd_set_option_attributes(self, item, oe, oo, value); - if (o != NULL) - style_update_new(oo, o->name, oe->style); - break; + if ((number = attributes_fromstring(value)) == -1) { + cmdq_error(item, "bad attributes: %s", value); + return (-1); + } + o = options_set_number(oo, oe->name, number); + options_style_update_new(oo, o); + return (0); case OPTIONS_TABLE_FLAG: - o = cmd_set_option_flag(self, item, oe, oo, value); - break; + return (cmd_set_option_flag(item, oe, oo, value)); case OPTIONS_TABLE_CHOICE: - o = cmd_set_option_choice(self, item, oe, oo, value); - break; + return (cmd_set_option_choice(item, oe, oo, value)); case OPTIONS_TABLE_STYLE: - o = cmd_set_option_style(self, item, oe, oo, value); + o = options_set_style(oo, oe->name, append, value); + if (o == NULL) { + cmdq_error(item, "bad style: %s", value); + return (-1); + } + options_style_update_old(oo, o); + return (0); + case OPTIONS_TABLE_ARRAY: break; } - if (o == NULL) - return (-1); - return (0); + return (-1); } -/* Set a string option. */ -static struct options_entry * -cmd_set_option_string(struct cmd *self, __unused struct cmdq_item *item, - const struct options_table_entry *oe, struct options *oo, - const char *value) -{ - struct args *args = self->args; - int append = args_has(args, 'a'); - - return (options_set_string(oo, oe->name, append, "%s", value)); -} - -/* Set a number option. */ -static struct options_entry * -cmd_set_option_number(__unused struct cmd *self, struct cmdq_item *item, - const struct options_table_entry *oe, struct options *oo, - const char *value) -{ - long long ll; - const char *errstr; - - ll = strtonum(value, oe->minimum, oe->maximum, &errstr); - if (errstr != NULL) { - cmdq_error(item, "value is %s: %s", errstr, value); - return (NULL); - } - - return (options_set_number(oo, oe->name, ll)); -} - -/* Set a key option. */ -static struct options_entry * -cmd_set_option_key(__unused struct cmd *self, struct cmdq_item *item, - const struct options_table_entry *oe, struct options *oo, - const char *value) -{ - key_code key; - - key = key_string_lookup_string(value); - if (key == KEYC_UNKNOWN) { - cmdq_error(item, "bad key: %s", value); - return (NULL); - } - - return (options_set_number(oo, oe->name, key)); -} - -/* Set a colour option. */ -static struct options_entry * -cmd_set_option_colour(__unused struct cmd *self, struct cmdq_item *item, - const struct options_table_entry *oe, struct options *oo, - const char *value) -{ - int colour; - - if ((colour = colour_fromstring(value)) == -1) { - cmdq_error(item, "bad colour: %s", value); - return (NULL); - } - - return (options_set_number(oo, oe->name, colour)); -} - -/* Set an attributes option. */ -static struct options_entry * -cmd_set_option_attributes(__unused struct cmd *self, struct cmdq_item *item, - const struct options_table_entry *oe, struct options *oo, - const char *value) -{ - int attr; - - if ((attr = attributes_fromstring(value)) == -1) { - cmdq_error(item, "bad attributes: %s", value); - return (NULL); - } - - return (options_set_number(oo, oe->name, attr)); -} - -/* Set a flag option. */ -static struct options_entry * -cmd_set_option_flag(__unused struct cmd *self, struct cmdq_item *item, +static int +cmd_set_option_flag(struct cmdq_item *item, const struct options_table_entry *oe, struct options *oo, const char *value) { @@ -473,31 +337,28 @@ cmd_set_option_flag(__unused struct cmd *self, struct cmdq_item *item, if (value == NULL || *value == '\0') flag = !options_get_number(oo, oe->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 { - if ((value[0] == '1' && value[1] == '\0') || - strcasecmp(value, "on") == 0 || - strcasecmp(value, "yes") == 0) - flag = 1; - else if ((value[0] == '0' && value[1] == '\0') || - strcasecmp(value, "off") == 0 || - strcasecmp(value, "no") == 0) - flag = 0; - else { - cmdq_error(item, "bad value: %s", value); - return (NULL); - } + cmdq_error(item, "bad value: %s", value); + return (-1); } - - return (options_set_number(oo, oe->name, flag)); + options_set_number(oo, oe->name, flag); + return (0); } -/* Set a choice option. */ -static struct options_entry * -cmd_set_option_choice(__unused struct cmd *self, struct cmdq_item *item, +static int +cmd_set_option_choice(struct cmdq_item *item, const struct options_table_entry *oe, struct options *oo, const char *value) { - const char **choicep; + const char **cp; int n, choice = -1; if (value == NULL) { @@ -506,41 +367,16 @@ cmd_set_option_choice(__unused struct cmd *self, struct cmdq_item *item, choice = !choice; } else { n = 0; - for (choicep = oe->choices; *choicep != NULL; choicep++) { + for (cp = oe->choices; *cp != NULL; cp++) { + if (strcmp(*cp, value) == 0) + choice = n; n++; - if (strncmp(*choicep, value, strlen(value)) != 0) - continue; - - if (choice != -1) { - cmdq_error(item, "ambiguous value: %s", value); - return (NULL); - } - choice = n - 1; } if (choice == -1) { cmdq_error(item, "unknown value: %s", value); - return (NULL); + return (-1); } } - - return (options_set_number(oo, oe->name, choice)); -} - -/* Set a style option. */ -static struct options_entry * -cmd_set_option_style(struct cmd *self, struct cmdq_item *item, - const struct options_table_entry *oe, struct options *oo, - const char *value) -{ - struct args *args = self->args; - int append = args_has(args, 'a'); - struct options_entry *o; - - if ((o = options_set_style(oo, oe->name, append, value)) == NULL) { - cmdq_error(item, "bad style: %s", value); - return (NULL); - } - - style_update_old(oo, oe->name, &o->style); - return (o); + options_set_number(oo, oe->name, choice); + return (0); } diff --git a/cmd-show-options.c b/cmd-show-options.c index 93ae939f..67a70330 100644 --- a/cmd-show-options.c +++ b/cmd-show-options.c @@ -20,6 +20,7 @@ #include #include +#include #include "tmux.h" @@ -30,9 +31,9 @@ static enum cmd_retval cmd_show_options_exec(struct cmd *, struct cmdq_item *); static enum cmd_retval cmd_show_options_one(struct cmd *, struct cmdq_item *, - struct options *, int); + struct options *); static enum cmd_retval cmd_show_options_all(struct cmd *, struct cmdq_item *, - struct options *, enum options_table_scope); + struct options *); const struct cmd_entry cmd_show_options_entry = { .name = "show-options", @@ -64,134 +65,106 @@ static enum cmd_retval cmd_show_options_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = self->args; - struct session *s = item->state.tflag.s; - struct winlink *wl = item->state.tflag.wl; + struct cmd_find_state *fs = &item->state.tflag; struct options *oo; enum options_table_scope scope; - int quiet; - const char *target; + char *cause; + int window; - if (args_has(self->args, 's')) { - oo = global_options; - scope = OPTIONS_TABLE_SERVER; - } else if (args_has(self->args, 'w') || - self->entry == &cmd_show_window_options_entry) { - scope = OPTIONS_TABLE_WINDOW; - if (args_has(self->args, 'g')) - oo = global_w_options; - else if (wl == NULL) { - target = args_get(args, 't'); - if (target != NULL) { - cmdq_error(item, "no such window: %s", target); - } else - cmdq_error(item, "no current window"); - return (CMD_RETURN_ERROR); - } else - oo = wl->window->options; - } else { - scope = OPTIONS_TABLE_SESSION; - if (args_has(self->args, 'g')) - oo = global_s_options; - else if (s == NULL) { - target = args_get(args, 't'); - if (target != NULL) { - cmdq_error(item, "no such session: %s", target); - } else - cmdq_error(item, "no current session"); - return (CMD_RETURN_ERROR); - } else - oo = s->options; + window = (self->entry == &cmd_show_window_options_entry); + scope = options_scope_from_flags(args, window, fs, &oo, &cause); + if (scope == OPTIONS_TABLE_NONE) { + cmdq_error(item, "%s", cause); + free(cause); + return (CMD_RETURN_ERROR); } - quiet = args_has(self->args, 'q'); if (args->argc == 0) - return (cmd_show_options_all(self, item, oo, scope)); + return (cmd_show_options_all(self, item, oo)); else - return (cmd_show_options_one(self, item, oo, quiet)); + return (cmd_show_options_one(self, item, oo)); +} + +static void +cmd_show_options_print(struct cmd *self, struct cmdq_item *item, + struct option *o, int idx) +{ + const char *name; + const char *value; + char *tmp, *escaped; + + if (idx != -1) { + xasprintf(&tmp, "%s[%d]", options_name(o), idx); + name = tmp; + } else { + tmp = NULL; + name = options_name(o); + } + + value = options_tostring(o, idx); + if (args_has(self->args, 'v')) + cmdq_print(item, "%s", value); + else if (options_isstring(o)) { + stravis(&escaped, value, VIS_OCTAL|VIS_TAB|VIS_NL|VIS_DQ); + cmdq_print(item, "%s \"%s\"", name, escaped); + free(escaped); + } else + cmdq_print(item, "%s %s", name, value); + + free(tmp); } static enum cmd_retval cmd_show_options_one(struct cmd *self, struct cmdq_item *item, - struct options *oo, int quiet) + struct options *oo) { - struct args *args = self->args; - const char *name = args->argv[0]; - const struct options_table_entry *oe; - struct options_entry *o; - const char *optval; + struct args *args = self->args; + struct option *o; + int idx, ambiguous; + const char *name = args->argv[0]; -retry: - if (*name == '@') { - if ((o = options_find1(oo, name)) == NULL) { - if (quiet) - return (CMD_RETURN_NORMAL); - cmdq_error(item, "unknown option: %s", name); + o = options_match_get(oo, name, &idx, 1, &ambiguous); + if (o == NULL) { + if (args_has(args, 'q')) + return (CMD_RETURN_NORMAL); + if (ambiguous) { + cmdq_error(item, "ambiguous option: %s", name); return (CMD_RETURN_ERROR); } - if (args_has(self->args, 'v')) - cmdq_print(item, "%s", o->str); - else - cmdq_print(item, "%s \"%s\"", o->name, o->str); - return (CMD_RETURN_NORMAL); - } - - oe = NULL; - if (options_table_find(name, &oe) != 0) { - cmdq_error(item, "ambiguous option: %s", name); - return (CMD_RETURN_ERROR); - } - if (oe == NULL) { - if (quiet) + if (options_match_get(oo, name, &idx, 0, &ambiguous) != NULL) return (CMD_RETURN_NORMAL); cmdq_error(item, "unknown option: %s", name); return (CMD_RETURN_ERROR); } - if (oe->style != NULL) { - name = oe->style; - goto retry; - } - if ((o = options_find1(oo, oe->name)) == NULL) - return (CMD_RETURN_NORMAL); - optval = options_table_print_entry(oe, o, args_has(self->args, 'v')); - if (args_has(self->args, 'v')) - cmdq_print(item, "%s", optval); - else - cmdq_print(item, "%s %s", oe->name, optval); + cmd_show_options_print(self, item, o, idx); return (CMD_RETURN_NORMAL); } static enum cmd_retval cmd_show_options_all(struct cmd *self, struct cmdq_item *item, - struct options *oo, enum options_table_scope scope) + struct options *oo) { - const struct options_table_entry *oe; - struct options_entry *o; - const char *optval; - int vflag; + struct option *o; + const struct options_table_entry *oe; + u_int size, idx; o = options_first(oo); while (o != NULL) { - if (*o->name == '@') { - if (args_has(self->args, 'v')) - cmdq_print(item, "%s", o->str); - else - cmdq_print(item, "%s \"%s\"", o->name, o->str); + oe = options_table_entry(o); + if (oe != NULL && oe->style != NULL) { + o = options_next(o); + continue; + } + if (options_array_size(o, &size) == -1) + cmd_show_options_print(self, item, o, -1); + else { + for (idx = 0; idx < size; idx++) { + if (options_array_get(o, idx) == NULL) + continue; + cmd_show_options_print(self, item, o, idx); + } } o = options_next(o); } - - vflag = args_has(self->args, 'v'); - for (oe = options_table; oe->name != NULL; oe++) { - if (oe->style != NULL || oe->scope != scope) - continue; - if ((o = options_find1(oo, oe->name)) == NULL) - continue; - optval = options_table_print_entry(oe, o, vflag); - if (vflag) - cmdq_print(item, "%s", optval); - else - cmdq_print(item, "%s %s", oe->name, optval); - } - return (CMD_RETURN_NORMAL); } diff --git a/format.c b/format.c index cc270bc0..e67aaedc 100644 --- a/format.c +++ b/format.c @@ -640,39 +640,29 @@ static char * format_find(struct format_tree *ft, const char *key, int modifiers) { struct format_entry *fe, fe_find; - struct options_entry *o; struct environ_entry *envent; static char s[64]; + struct option *o; const char *found; + int idx; char *copy, *saved; - found = NULL; - if (~modifiers & FORMAT_TIMESTRING) { - o = options_find(global_options, key); + o = options_parse_get(global_options, key, &idx, 0); if (o == NULL && ft->w != NULL) - o = options_find(ft->w->options, key); + o = options_parse_get(ft->w->options, key, &idx, 0); if (o == NULL) - o = options_find(global_w_options, key); + o = options_parse_get(global_w_options, key, &idx, 0); if (o == NULL && ft->s != NULL) - o = options_find(ft->s->options, key); + o = options_parse_get(ft->s->options, key, &idx, 0); if (o == NULL) - o = options_find(global_s_options, key); + o = options_parse_get(global_s_options, key, &idx, 0); if (o != NULL) { - switch (o->type) { - case OPTIONS_STRING: - found = o->str; - goto found; - case OPTIONS_NUMBER: - xsnprintf(s, sizeof s, "%lld", o->num); - found = s; - goto found; - case OPTIONS_STYLE: - found = style_tostring(&o->style); - goto found; - } + found = options_tostring(o, idx); + goto found; } } + found = NULL; fe_find.key = (char *) key; fe = RB_FIND(format_entry_tree, &ft->tree, &fe_find); diff --git a/options-table.c b/options-table.c index dc9fbdd7..5eb2ad5d 100644 --- a/options-table.c +++ b/options-table.c @@ -54,7 +54,7 @@ static const char *options_table_pane_status_list[] = { "off", "top", "bottom", NULL }; -/* Server options. */ +/* Top-level options. */ const struct options_table_entry options_table[] = { { .name = "buffer-limit", .type = OPTIONS_TABLE_NUMBER, @@ -104,12 +104,6 @@ const struct options_table_entry options_table[] = { .default_num = 100 }, - { .name = "quiet", - .type = OPTIONS_TABLE_FLAG, - .scope = OPTIONS_TABLE_SERVER, - .default_num = 0 - }, - { .name = "set-clipboard", .type = OPTIONS_TABLE_FLAG, .scope = OPTIONS_TABLE_SERVER, @@ -900,100 +894,3 @@ const struct options_table_entry options_table[] = { { .name = NULL } }; - -/* Populate an options tree from a table. */ -void -options_table_populate_tree(enum options_table_scope scope, struct options *oo) -{ - const struct options_table_entry *oe; - - for (oe = options_table; oe->name != NULL; oe++) { - if (oe->scope == OPTIONS_TABLE_NONE) - fatalx("no scope for %s", oe->name); - if (oe->scope != scope) - continue; - switch (oe->type) { - case OPTIONS_TABLE_STRING: - options_set_string(oo, oe->name, 0, "%s", - oe->default_str); - break; - case OPTIONS_TABLE_STYLE: - options_set_style(oo, oe->name, 0, oe->default_str); - break; - default: - options_set_number(oo, oe->name, oe->default_num); - break; - } - } -} - -/* Print an option using its type from the table. */ -const char * -options_table_print_entry(const struct options_table_entry *oe, - struct options_entry *o, int no_quotes) -{ - static char out[BUFSIZ]; - const char *s; - - *out = '\0'; - switch (oe->type) { - case OPTIONS_TABLE_STRING: - if (no_quotes) - xsnprintf(out, sizeof out, "%s", o->str); - else - xsnprintf(out, sizeof out, "\"%s\"", o->str); - break; - case OPTIONS_TABLE_NUMBER: - xsnprintf(out, sizeof out, "%lld", o->num); - break; - case OPTIONS_TABLE_KEY: - s = key_string_lookup_key(o->num); - xsnprintf(out, sizeof out, "%s", s); - break; - case OPTIONS_TABLE_COLOUR: - s = colour_tostring(o->num); - xsnprintf(out, sizeof out, "%s", s); - break; - case OPTIONS_TABLE_ATTRIBUTES: - s = attributes_tostring(o->num); - xsnprintf(out, sizeof out, "%s", s); - break; - case OPTIONS_TABLE_FLAG: - if (o->num) - strlcpy(out, "on", sizeof out); - else - strlcpy(out, "off", sizeof out); - break; - case OPTIONS_TABLE_CHOICE: - s = oe->choices[o->num]; - xsnprintf(out, sizeof out, "%s", s); - break; - case OPTIONS_TABLE_STYLE: - s = style_tostring(&o->style); - xsnprintf(out, sizeof out, "%s", s); - break; - } - return (out); -} - -/* Find an option. */ -int -options_table_find(const char *optstr, const struct options_table_entry **oe) -{ - const struct options_table_entry *oe_loop; - - for (oe_loop = options_table; oe_loop->name != NULL; oe_loop++) { - if (strncmp(oe_loop->name, optstr, strlen(optstr)) != 0) - continue; - - /* If already found, ambiguous. */ - if (*oe != NULL) - return (-1); - *oe = oe_loop; - - /* Bail now if an exact match. */ - if (strcmp(oe_loop->name, optstr) == 0) - break; - } - return (0); -} diff --git a/options.c b/options.c index 811606b2..258f2981 100644 --- a/options.c +++ b/options.c @@ -18,6 +18,7 @@ #include +#include #include #include #include @@ -29,18 +30,72 @@ * a red-black tree. */ -struct options { - RB_HEAD(options_tree, options_entry) tree; - struct options *parent; +struct option { + struct options *owner; + + const char *name; + const struct options_table_entry *tableentry; + + union { + char *string; + long long number; + struct grid_cell style; + struct { + const char **array; + u_int arraysize; + }; + }; + + RB_ENTRY(option) entry; }; -static int options_cmp(struct options_entry *, struct options_entry *); -RB_GENERATE_STATIC(options_tree, options_entry, entry, options_cmp); +struct options { + RB_HEAD(options_tree, option) tree; + struct options *parent; +}; + +static struct option *options_add(struct options *, const char *); + +#define OPTIONS_ARRAY_LIMIT 1000 + +#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_ATTRIBUTES || \ + (o)->tableentry->type == OPTIONS_TABLE_FLAG || \ + (o)->tableentry->type == OPTIONS_TABLE_CHOICE)) +#define OPTIONS_IS_STYLE(o) \ + ((o)->tableentry != NULL && \ + (o)->tableentry->type == OPTIONS_TABLE_STYLE) +#define OPTIONS_IS_ARRAY(o) \ + ((o)->tableentry != NULL && \ + (o)->tableentry->type == OPTIONS_TABLE_ARRAY) + +static int options_cmp(struct option *, struct option *); +RB_GENERATE_STATIC(options_tree, option, entry, options_cmp); static int -options_cmp(struct options_entry *o1, struct options_entry *o2) +options_cmp(struct option *lhs, struct option *rhs) { - return (strcmp(o1->name, o2->name)); + return (strcmp(lhs->name, rhs->name)); +} + +static const struct options_table_entry * +options_parent_table_entry(struct options *oo, const char *s) +{ + struct option *o; + + if (oo->parent == NULL) + fatalx("no parent options for %s", s); + o = options_get_only(oo->parent, s); + if (o == NULL) + fatalx("%s not in parent options", s); + return (o->tableentry); } struct options * @@ -54,181 +109,549 @@ options_create(struct options *parent) return (oo); } -static void -options_free1(struct options *oo, struct options_entry *o) -{ - RB_REMOVE(options_tree, &oo->tree, o); - free((char *)o->name); - if (o->type == OPTIONS_STRING) - free(o->str); - free(o); -} - -static struct options_entry * -options_new(struct options *oo, const char *name) -{ - struct options_entry *o; - - if ((o = options_find1(oo, name)) == NULL) { - o = xmalloc(sizeof *o); - o->name = xstrdup(name); - RB_INSERT(options_tree, &oo->tree, o); - memcpy(&o->style, &grid_default_cell, sizeof o->style); - } else if (o->type == OPTIONS_STRING) - free(o->str); - return (o); -} - void options_free(struct options *oo) { - struct options_entry *o, *o1; + struct option *o, *tmp; - RB_FOREACH_SAFE (o, options_tree, &oo->tree, o1) - options_free1(oo, o); + RB_FOREACH_SAFE (o, options_tree, &oo->tree, tmp) + options_remove(o); free(oo); } -struct options_entry * +struct option * options_first(struct options *oo) { return (RB_MIN(options_tree, &oo->tree)); } -struct options_entry * -options_next(struct options_entry *o) +struct option * +options_next(struct option *o) { return (RB_NEXT(options_tree, &oo->tree, o)); } -struct options_entry * -options_find1(struct options *oo, const char *name) +struct option * +options_get_only(struct options *oo, const char *name) { - struct options_entry p; + struct option o; - p.name = (char *)name; - return (RB_FIND(options_tree, &oo->tree, &p)); + o.name = name; + return (RB_FIND(options_tree, &oo->tree, &o)); } -struct options_entry * -options_find(struct options *oo, const char *name) +struct option * +options_get(struct options *oo, const char *name) { - struct options_entry *o, p; + struct option *o; - p.name = (char *)name; - o = RB_FIND(options_tree, &oo->tree, &p); + o = options_get_only(oo, name); while (o == NULL) { oo = oo->parent; if (oo == NULL) break; - o = RB_FIND(options_tree, &oo->tree, &p); + o = options_get_only(oo, name); } return (o); } +struct option * +options_empty(struct options *oo, const struct options_table_entry *oe) +{ + struct option *o; + + o = options_add(oo, oe->name); + o->tableentry = oe; + + return (o); +} + +struct option * +options_default(struct options *oo, const struct options_table_entry *oe) +{ + struct option *o; + char *cp, *copy, *next; + u_int idx = 0; + + o = options_empty(oo, oe); + + if (oe->type == OPTIONS_TABLE_ARRAY) { + copy = cp = xstrdup(oe->default_str); + while ((next = strsep(&cp, ",")) != NULL) { + options_array_set(o, idx, next); + idx++; + } + free(copy); + return (o); + } + + if (oe->type == OPTIONS_TABLE_STRING) + o->string = xstrdup(oe->default_str); + else if (oe->type == OPTIONS_TABLE_STYLE) { + memcpy(&o->style, &grid_default_cell, sizeof o->style); + style_parse(&grid_default_cell, &o->style, oe->default_str); + } else + o->number = oe->default_num; + return (o); +} + +static struct option * +options_add(struct options *oo, const char *name) +{ + struct option *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); +} + void -options_remove(struct options *oo, const char *name) +options_remove(struct option *o) { - struct options_entry *o; + struct options *oo = o->owner; + u_int i; - if ((o = options_find1(oo, name)) != NULL) - options_free1(oo, o); -} - -struct options_entry * -options_set_string(struct options *oo, const char *name, int append, - const char *fmt, ...) -{ - struct options_entry *o; - va_list ap; - char *s, *value; - - va_start(ap, fmt); - xvasprintf(&s, fmt, ap); - va_end(ap); - - o = options_find1(oo, name); - if (o == NULL || !append) - value = s; - else { - xasprintf(&value, "%s%s", s, o->str); - free(s); + if (OPTIONS_IS_STRING(o)) + free((void *)o->string); + else if (OPTIONS_IS_ARRAY(o)) { + for (i = 0; i < o->arraysize; i++) + free((void *)o->array[i]); + free(o->array); } - o = options_new(oo, name); - o->type = OPTIONS_STRING; - o->str = value; + RB_REMOVE(options_tree, &oo->tree, o); + free(o); +} +const char * +options_name(struct option *o) +{ + return (o->name); +} + +const struct options_table_entry * +options_table_entry(struct option *o) +{ + return (o->tableentry); +} + +const char * +options_array_get(struct option *o, u_int idx) +{ + if (!OPTIONS_IS_ARRAY(o)) + return (NULL); + if (idx >= o->arraysize) + return (NULL); + return (o->array[idx]); +} + +int +options_array_set(struct option *o, u_int idx, const char *value) +{ + u_int i; + + if (!OPTIONS_IS_ARRAY(o)) + return (-1); + + if (idx >= OPTIONS_ARRAY_LIMIT) + return (-1); + if (idx >= o->arraysize) { + o->array = xreallocarray(o->array, idx + 1, sizeof *o->array); + for (i = o->arraysize; i < idx + 1; i++) + o->array[i] = NULL; + o->arraysize = idx + 1; + } + if (o->array[idx] != NULL) + free((void *)o->array[idx]); + if (value != NULL) + o->array[idx] = xstrdup(value); + else + o->array[idx] = NULL; + return (0); +} + +int +options_array_size(struct option *o, u_int *size) +{ + if (!OPTIONS_IS_ARRAY(o)) + return (-1); + if (size != NULL) + *size = o->arraysize; + return (0); +} + +int +options_isstring(struct option *o) +{ + if (o->tableentry == NULL) + return (1); + return (OPTIONS_IS_STRING(o) || OPTIONS_IS_ARRAY(o)); +} + +const char * +options_tostring(struct option *o, int idx) +{ + static char s[1024]; + const char *tmp; + + if (OPTIONS_IS_ARRAY(o)) { + if (idx == -1) + return (NULL); + if ((u_int)idx >= o->arraysize || o->array[idx] == NULL) + return (""); + return (o->array[idx]); + } + if (OPTIONS_IS_STYLE(o)) + return (style_tostring(&o->style)); + if (OPTIONS_IS_NUMBER(o)) { + tmp = NULL; + switch (o->tableentry->type) { + case OPTIONS_TABLE_NUMBER: + xsnprintf(s, sizeof s, "%lld", o->number); + break; + case OPTIONS_TABLE_KEY: + tmp = key_string_lookup_key(o->number); + break; + case OPTIONS_TABLE_COLOUR: + tmp = colour_tostring(o->number); + break; + case OPTIONS_TABLE_ATTRIBUTES: + tmp = attributes_tostring(o->number); + break; + case OPTIONS_TABLE_FLAG: + tmp = (o->number ? "on" : "off"); + break; + case OPTIONS_TABLE_CHOICE: + tmp = o->tableentry->choices[o->number]; + break; + case OPTIONS_TABLE_STRING: + case OPTIONS_TABLE_STYLE: + case OPTIONS_TABLE_ARRAY: + break; + } + if (tmp != NULL) + xsnprintf(s, sizeof s, "%s", tmp); + return (s); + } + if (OPTIONS_IS_STRING(o)) + return (o->string); + return (NULL); +} + +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 option * +options_parse_get(struct options *oo, const char *s, int *idx, int only) +{ + struct option *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); + if (o != NULL) { + if (OPTIONS_IS_ARRAY(o) && *idx == -1) + return (NULL); + if (!OPTIONS_IS_ARRAY(o) && *idx != -1) + return (NULL); + } return (o); } +char * +options_match(const char *s, int *idx, int* ambiguous) +{ + const struct options_table_entry *oe, *found; + char *name; + size_t namelen; + + name = options_parse(s, idx); + 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(name); + return (NULL); + } + found = oe; + } + } + free(name); + if (found == NULL) { + *ambiguous = 0; + return (NULL); + } + return (xstrdup(found->name)); +} + +struct option * +options_match_get(struct options *oo, const char *s, int *idx, int only, + int* ambiguous) +{ + char *name; + struct option *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); + if (o != NULL) { + if (OPTIONS_IS_ARRAY(o) && *idx == -1) + return (NULL); + if (!OPTIONS_IS_ARRAY(o) && *idx != -1) + return (NULL); + } + return (o); +} + + const char * options_get_string(struct options *oo, const char *name) { - struct options_entry *o; + struct option *o; - if ((o = options_find(oo, name)) == NULL) + o = options_get(oo, name); + if (o == NULL) fatalx("missing option %s", name); - if (o->type != OPTIONS_STRING) - fatalx("option %s not a string", name); - return (o->str); -} - -struct options_entry * -options_set_number(struct options *oo, const char *name, long long value) -{ - struct options_entry *o; - - o = options_new(oo, name); - o->type = OPTIONS_NUMBER; - o->num = value; - - return (o); + if (!OPTIONS_IS_STRING(o)) + fatalx("option %s is not a string", name); + return (o->string); } long long options_get_number(struct options *oo, const char *name) { - struct options_entry *o; + struct option *o; - if ((o = options_find(oo, name)) == NULL) + o = options_get(oo, name); + if (o == NULL) fatalx("missing option %s", name); - if (o->type != OPTIONS_NUMBER) - fatalx("option %s not a number", name); - return (o->num); -} - -struct options_entry * -options_set_style(struct options *oo, const char *name, int append, - const char *value) -{ - struct options_entry *o; - struct grid_cell tmpgc; - - o = options_find1(oo, name); - if (o == NULL || !append) - memcpy(&tmpgc, &grid_default_cell, sizeof tmpgc); - else - memcpy(&tmpgc, &o->style, sizeof tmpgc); - - if (style_parse(&grid_default_cell, &tmpgc, value) == -1) - return (NULL); - - o = options_new(oo, name); - o->type = OPTIONS_STYLE; - memcpy(&o->style, &tmpgc, sizeof o->style); - - return (o); + if (!OPTIONS_IS_NUMBER(o)) + fatalx("option %s is not a number", name); + return (o->number); } const struct grid_cell * options_get_style(struct options *oo, const char *name) { - struct options_entry *o; + struct option *o; - if ((o = options_find(oo, name)) == NULL) + o = options_get(oo, name); + if (o == NULL) fatalx("missing option %s", name); - if (o->type != OPTIONS_STYLE) - fatalx("option %s not a style", name); + if (!OPTIONS_IS_STYLE(o)) + fatalx("option %s is not a style", name); return (&o->style); } + +struct option * +options_set_string(struct options *oo, const char *name, int append, + const char *fmt, ...) +{ + struct option *o; + va_list ap; + 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)) { + xasprintf(&value, "%s%s", o->string, 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->string); + o->string = value; + return (o); +} + +struct option * +options_set_number(struct options *oo, const char *name, long long value) +{ + struct option *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->number = value; + return (o); +} + +struct option * +options_set_style(struct options *oo, const char *name, int append, + const char *value) +{ + struct option *o; + struct grid_cell gc; + + if (*name == '@') + fatalx("user option %s must be a string", name); + + o = options_get_only(oo, name); + if (o != NULL && append && OPTIONS_IS_STYLE(o)) + memcpy(&gc, &o->style, sizeof gc); + else + memcpy(&gc, &grid_default_cell, sizeof gc); + if (style_parse(&grid_default_cell, &gc, value) == -1) + return (NULL); + if (o == NULL) { + o = options_default(oo, options_parent_table_entry(oo, name)); + if (o == NULL) + return (NULL); + } + + if (!OPTIONS_IS_STYLE(o)) + fatalx("option %s is not a style", name); + memcpy(&o->style, &gc, sizeof o->style); + return (o); +} + +enum options_table_scope +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; + const char *target= args_get(args, 't'); + + if (args_has(args, 's')) { + *oo = global_options; + return (OPTIONS_TABLE_SERVER); + } + + 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); + } +} + +void +options_style_update_new(struct options *oo, struct option *o) +{ + const char *newname = o->tableentry->style; + struct option *new; + + if (newname == NULL) + return; + new = options_get_only(oo, newname); + if (new == NULL) + new = options_set_style(oo, newname, 0, "default"); + + if (strstr(o->name, "-bg") != NULL) + new->style.bg = o->number; + else if (strstr(o->name, "-fg") != NULL) + new->style.fg = o->number; + else if (strstr(o->name, "-attr") != NULL) + new->style.attr = o->number; +} + +void +options_style_update_old(struct options *oo, struct option *o) +{ + char newname[128]; + int size; + + size = strrchr(o->name, '-') - o->name; + + xsnprintf(newname, sizeof newname, "%.*s-bg", size, o->name); + options_set_number(oo, newname, o->style.bg); + + xsnprintf(newname, sizeof newname, "%.*s-fg", size, o->name); + options_set_number(oo, newname, o->style.fg); + + xsnprintf(newname, sizeof newname, "%.*s-attr", size, o->name); + options_set_number(oo, newname, o->style.attr); +} diff --git a/style.c b/style.c index 26bb75a9..cec1b894 100644 --- a/style.c +++ b/style.c @@ -129,55 +129,6 @@ style_tostring(struct grid_cell *gc) return (s); } -/* Synchronize new -style option with the old one. */ -void -style_update_new(struct options *oo, const char *name, const char *newname) -{ - int value; - struct grid_cell *gc; - struct options_entry *o; - - /* It's a colour or attribute, but with no -style equivalent. */ - if (newname == NULL) - return; - - o = options_find1(oo, newname); - if (o == NULL) - o = options_set_style(oo, newname, 0, "default"); - gc = &o->style; - - o = options_find1(oo, name); - if (o == NULL) - o = options_set_number(oo, name, 8); - value = o->num; - - if (strstr(name, "-bg") != NULL) - gc->bg = value; - else if (strstr(name, "-fg") != NULL) - gc->fg = value; - else if (strstr(name, "-attr") != NULL) - gc->attr = value; -} - -/* Synchronize all the old options with the new -style one. */ -void -style_update_old(struct options *oo, const char *name, struct grid_cell *gc) -{ - char newname[128]; - int size; - - size = strrchr(name, '-') - name; - - xsnprintf(newname, sizeof newname, "%.*s-bg", size, name); - options_set_number(oo, newname, gc->bg); - - xsnprintf(newname, sizeof newname, "%.*s-fg", size, name); - options_set_number(oo, newname, gc->fg); - - xsnprintf(newname, sizeof newname, "%.*s-attr", size, name); - options_set_number(oo, newname, gc->attr); -} - /* Apply a style. */ void style_apply(struct grid_cell *gc, struct options *oo, const char *name) diff --git a/tmux.c b/tmux.c index ad3272b0..7a513f01 100644 --- a/tmux.c +++ b/tmux.c @@ -187,9 +187,11 @@ find_home(void) int main(int argc, char **argv) { - char *path, *label, **var, tmp[PATH_MAX], *shellcmd = NULL; - const char *s; - int opt, flags, keys; + char *path, *label, tmp[PATH_MAX]; + char *shellcmd = NULL, **var; + const char *s, *shell; + int opt, flags, keys; + const struct options_table_entry *oe; if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL) { if (setlocale(LC_CTYPE, "") == NULL) @@ -295,15 +297,23 @@ main(int argc, char **argv) environ_set(global_environ, "PWD", "%s", tmp); global_options = options_create(NULL); - options_table_populate_tree(OPTIONS_TABLE_SERVER, global_options); - global_s_options = options_create(NULL); - options_table_populate_tree(OPTIONS_TABLE_SESSION, global_s_options); - options_set_string(global_s_options, "default-shell", 0, "%s", - getshell()); - global_w_options = options_create(NULL); - options_table_populate_tree(OPTIONS_TABLE_WINDOW, global_w_options); + for (oe = options_table; oe->name != NULL; oe++) { + if (oe->scope == OPTIONS_TABLE_SERVER) + options_default(global_options, oe); + if (oe->scope == OPTIONS_TABLE_SESSION) + options_default(global_s_options, oe); + if (oe->scope == OPTIONS_TABLE_WINDOW) + options_default(global_w_options, oe); + } + + /* + * The default shell comes from SHELL or from the user's passwd entry + * if available. + */ + shell = getshell(); + options_set_string(global_s_options, "default-shell", 0, "%s", shell); /* Override keys to vi if VISUAL or EDITOR are set. */ if ((s = getenv("VISUAL")) != NULL || (s = getenv("EDITOR")) != NULL) { diff --git a/tmux.h b/tmux.h index 6a925ebc..dd4c22ee 100644 --- a/tmux.h +++ b/tmux.h @@ -643,23 +643,6 @@ struct hook { RB_ENTRY(hook) entry; }; -/* Option data structures. */ -struct options_entry { - const char *name; - - enum { - OPTIONS_STRING, - OPTIONS_NUMBER, - OPTIONS_STYLE - } type; - - char *str; - long long num; - struct grid_cell style; - - RB_ENTRY(options_entry) entry; -}; - /* Scheduled job. */ struct job { enum { @@ -1479,7 +1462,8 @@ enum options_table_type { OPTIONS_TABLE_ATTRIBUTES, OPTIONS_TABLE_FLAG, OPTIONS_TABLE_CHOICE, - OPTIONS_TABLE_STYLE + OPTIONS_TABLE_STYLE, + OPTIONS_TABLE_ARRAY, }; enum options_table_scope { OPTIONS_TABLE_NONE, @@ -1623,29 +1607,45 @@ void notify_window(const char *, struct window *); void notify_pane(const char *, struct window_pane *); /* options.c */ -struct options *options_create(struct options *); -void options_free(struct options *); -struct options_entry *options_first(struct options *); -struct options_entry *options_next(struct options_entry *); -struct options_entry *options_find1(struct options *, const char *); -struct options_entry *options_find(struct options *, const char *); -void options_remove(struct options *, const char *); -struct options_entry * printflike(4, 5) options_set_string(struct options *, - const char *, int, const char *, ...); -const char *options_get_string(struct options *, const char *); -struct options_entry *options_set_number(struct options *, const char *, - long long); -long long options_get_number(struct options *, const char *); -struct options_entry *options_set_style(struct options *, const char *, int, - const char *); +struct options *options_create(struct options *); +void options_free(struct options *); +struct option *options_first(struct options *); +struct option *options_next(struct option *); +struct option *options_empty(struct options *, + const struct options_table_entry *); +struct option *options_default(struct options *, + const struct options_table_entry *); +const char *options_name(struct option *); +const struct options_table_entry *options_table_entry(struct option *); +struct option *options_get_only(struct options *, const char *); +struct option *options_get(struct options *, const char *); +void options_remove(struct option *); +const char *options_array_get(struct option *, u_int); +int options_array_set(struct option *, u_int, const char *); +int options_array_size(struct option *, u_int *); +int options_isstring(struct option *); +const char *options_tostring(struct option *, int); +char *options_parse(const char *, int *); +struct option *options_parse_get(struct options *, const char *, int *, + int); +char *options_match(const char *, int *, int *); +struct option *options_match_get(struct options *, const char *, int *, + int, int *); +const char *options_get_string(struct options *, const char *); +long long options_get_number(struct options *, const char *); const struct grid_cell *options_get_style(struct options *, const char *); +struct option * printflike(4, 5) options_set_string(struct options *, + const char *, int, const char *, ...); +struct option *options_set_number(struct options *, const char *, long long); +struct option *options_set_style(struct options *, const char *, int, + const char *); +enum options_table_scope options_scope_from_flags(struct args *, int, + struct cmd_find_state *, struct options **, char **); +void options_style_update_new(struct options *, struct option *); +void options_style_update_old(struct options *, struct option *); /* options-table.c */ extern const struct options_table_entry options_table[]; -void options_table_populate_tree(enum options_table_scope, struct options *); -const char *options_table_print_entry(const struct options_table_entry *, - struct options_entry *, int); -int options_table_find(const char *, const struct options_table_entry **); /* job.c */ extern struct joblist all_jobs; @@ -2326,9 +2326,6 @@ __dead void printflike(1, 2) fatalx(const char *, ...); int style_parse(const struct grid_cell *, struct grid_cell *, const char *); const char *style_tostring(struct grid_cell *); -void style_update_new(struct options *, const char *, const char *); -void style_update_old(struct options *, const char *, - struct grid_cell *); void style_apply(struct grid_cell *, struct options *, const char *); void style_apply_update(struct grid_cell *, struct options *,