1 /* $OpenBSD: cmd-set-option.c,v 1.105 2017/01/15 20:48:41 nicm Exp $ */ 2 3 /* 4 * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/types.h> 20 21 #include <stdlib.h> 22 #include <string.h> 23 24 #include "tmux.h" 25 26 /* 27 * Set an option. 28 */ 29 30 static enum cmd_retval cmd_set_option_exec(struct cmd *, struct cmdq_item *); 31 32 static int cmd_set_option_set(struct cmd *, struct cmdq_item *, 33 struct options *, struct option *, const char *); 34 static int cmd_set_option_flag(struct cmdq_item *, 35 const struct options_table_entry *, struct options *, 36 const char *); 37 static int cmd_set_option_choice(struct cmdq_item *, 38 const struct options_table_entry *, struct options *, 39 const char *); 40 41 const struct cmd_entry cmd_set_option_entry = { 42 .name = "set-option", 43 .alias = "set", 44 45 .args = { "agoqst:uw", 1, 2 }, 46 .usage = "[-agosquw] [-t target-window] option [value]", 47 48 .tflag = CMD_WINDOW_CANFAIL, 49 50 .flags = CMD_AFTERHOOK, 51 .exec = cmd_set_option_exec 52 }; 53 54 const struct cmd_entry cmd_set_window_option_entry = { 55 .name = "set-window-option", 56 .alias = "setw", 57 58 .args = { "agoqt:u", 1, 2 }, 59 .usage = "[-agoqu] " CMD_TARGET_WINDOW_USAGE " option [value]", 60 61 .tflag = CMD_WINDOW_CANFAIL, 62 63 .flags = CMD_AFTERHOOK, 64 .exec = cmd_set_option_exec 65 }; 66 67 static enum cmd_retval 68 cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) 69 { 70 struct args *args = self->args; 71 struct cmd_find_state *fs = &item->state.tflag; 72 struct session *s = fs->s; 73 struct winlink *wl = fs->wl; 74 struct window *w = wl->window; 75 struct client *c; 76 enum options_table_scope scope; 77 struct options *oo; 78 struct option *parent, *o; 79 const char *name, *value, *target; 80 int window, idx, already, error, ambiguous; 81 char *cause; 82 83 /* Parse option name and index. */ 84 name = options_match(args->argv[0], &idx, &ambiguous); 85 if (name == NULL) { 86 if (ambiguous) 87 cmdq_error(item, "ambiguous option: %s", args->argv[0]); 88 else 89 cmdq_error(item, "invalid option: %s", args->argv[0]); 90 return (CMD_RETURN_ERROR); 91 } 92 if (args->argc < 2) 93 value = NULL; 94 else 95 value = args->argv[1]; 96 97 /* 98 * Figure out the scope: for user options it comes from the arguments, 99 * otherwise from the option name. 100 */ 101 if (*name == '@') { 102 window = (self->entry == &cmd_set_window_option_entry); 103 scope = options_scope_from_flags(args, window, fs, &oo, &cause); 104 } else { 105 if (options_get_only(global_options, name) != NULL) 106 scope = OPTIONS_TABLE_SERVER; 107 else if (options_get_only(global_s_options, name) != NULL) 108 scope = OPTIONS_TABLE_SESSION; 109 else if (options_get_only(global_w_options, name) != NULL) 110 scope = OPTIONS_TABLE_WINDOW; 111 else { 112 scope = OPTIONS_TABLE_NONE; 113 xasprintf(&cause, "unknown option: %s", args->argv[0]); 114 } 115 } 116 if (scope == OPTIONS_TABLE_NONE) { 117 cmdq_error(item, "%s", cause); 118 free(cause); 119 return (CMD_RETURN_ERROR); 120 } 121 122 /* Which table should this option go into? */ 123 if (scope == OPTIONS_TABLE_SERVER) 124 oo = global_options; 125 else if (scope == OPTIONS_TABLE_SESSION) { 126 if (args_has(self->args, 'g')) 127 oo = global_s_options; 128 else if (s == NULL) { 129 target = args_get(args, 't'); 130 if (target != NULL) 131 cmdq_error(item, "no such session: %s", target); 132 else 133 cmdq_error(item, "no current session"); 134 return (CMD_RETURN_ERROR); 135 } else 136 oo = s->options; 137 } else if (scope == OPTIONS_TABLE_WINDOW) { 138 if (args_has(self->args, 'g')) 139 oo = global_w_options; 140 else if (wl == NULL) { 141 target = args_get(args, 't'); 142 if (target != NULL) 143 cmdq_error(item, "no such window: %s", target); 144 else 145 cmdq_error(item, "no current window"); 146 return (CMD_RETURN_ERROR); 147 } else 148 oo = wl->window->options; 149 } 150 o = options_get_only(oo, name); 151 parent = options_get(oo, name); 152 153 /* Check that array options and indexes match up. */ 154 if (idx != -1) { 155 if (*name == '@' || options_array_size(parent, NULL) == -1) { 156 cmdq_error(item, "not an array: %s", args->argv[0]); 157 return (CMD_RETURN_ERROR); 158 } 159 } else { 160 if (*name != '@' && options_array_size(parent, NULL) != -1) { 161 cmdq_error(item, "is an array: %s", args->argv[0]); 162 return (CMD_RETURN_ERROR); 163 } 164 } 165 166 /* With -o, check this option is not already set. */ 167 if (!args_has(args, 'u') && args_has(args, 'o')) { 168 if (idx == -1) 169 already = (o != NULL); 170 else { 171 if (o == NULL) 172 already = 0; 173 else 174 already = (options_array_get(o, idx) != NULL); 175 } 176 if (already) { 177 if (args_has(args, 'q')) 178 return (CMD_RETURN_NORMAL); 179 cmdq_error(item, "already set: %s", args->argv[0]); 180 return (CMD_RETURN_ERROR); 181 } 182 } 183 184 /* Change the option. */ 185 if (args_has(args, 'u')) { 186 if (o == NULL) 187 return (CMD_RETURN_NORMAL); 188 if (idx == -1) { 189 if (oo == global_options || 190 oo == global_s_options || 191 oo == global_w_options) 192 options_default(oo, options_table_entry(o)); 193 else 194 options_remove(o); 195 } else 196 options_array_set(o, idx, NULL); 197 } else if (*name == '@') 198 options_set_string(oo, name, args_has(args, 'a'), "%s", value); 199 else if (idx == -1) { 200 error = cmd_set_option_set(self, item, oo, parent, value); 201 if (error != 0) 202 return (CMD_RETURN_ERROR); 203 } else { 204 if (o == NULL) 205 o = options_empty(oo, options_table_entry(parent)); 206 if (options_array_set(o, idx, value) != 0) { 207 cmdq_error(item, "invalid index: %s", args->argv[0]); 208 return (CMD_RETURN_ERROR); 209 } 210 } 211 212 /* Update timers and so on for various options. */ 213 if (strcmp(name, "automatic-rename") == 0) { 214 RB_FOREACH(w, windows, &windows) { 215 if (w->active == NULL) 216 continue; 217 if (options_get_number(w->options, "automatic-rename")) 218 w->active->flags |= PANE_CHANGED; 219 } 220 } 221 if (strcmp(name, "key-table") == 0) { 222 TAILQ_FOREACH(c, &clients, entry) 223 server_client_set_key_table(c, NULL); 224 } 225 if (strcmp(name, "status") == 0 || 226 strcmp(name, "status-interval") == 0) 227 status_timer_start_all(); 228 if (strcmp(name, "monitor-silence") == 0) 229 alerts_reset_all(); 230 if (strcmp(name, "window-style") == 0 || 231 strcmp(name, "window-active-style") == 0) { 232 RB_FOREACH(w, windows, &windows) 233 w->flags |= WINDOW_STYLECHANGED; 234 } 235 if (strcmp(name, "pane-border-status") == 0) { 236 RB_FOREACH(w, windows, &windows) 237 layout_fix_panes(w, w->sx, w->sy); 238 } 239 240 /* 241 * Update sizes and redraw. May not always be necessary but do it 242 * anyway. 243 */ 244 recalculate_sizes(); 245 TAILQ_FOREACH(c, &clients, entry) { 246 if (c->session != NULL) 247 server_redraw_client(c); 248 } 249 250 return (CMD_RETURN_NORMAL); 251 } 252 253 static int 254 cmd_set_option_set(struct cmd *self, struct cmdq_item *item, struct options *oo, 255 struct option *parent, const char *value) 256 { 257 const struct options_table_entry *oe; 258 struct args *args = self->args; 259 int append = args_has(args, 'a'); 260 struct option *o; 261 long long number; 262 const char *errstr; 263 key_code key; 264 265 oe = options_table_entry(parent); 266 if (value == NULL && 267 oe->type != OPTIONS_TABLE_FLAG && 268 oe->type != OPTIONS_TABLE_CHOICE) { 269 cmdq_error(item, "empty value"); 270 return (-1); 271 } 272 273 switch (oe->type) { 274 case OPTIONS_TABLE_STRING: 275 options_set_string(oo, oe->name, append, "%s", value); 276 return (0); 277 case OPTIONS_TABLE_NUMBER: 278 number = strtonum(value, oe->minimum, oe->maximum, &errstr); 279 if (errstr != NULL) { 280 cmdq_error(item, "value is %s: %s", errstr, value); 281 return (-1); 282 } 283 options_set_number(oo, oe->name, number); 284 return (0); 285 case OPTIONS_TABLE_KEY: 286 key = key_string_lookup_string(value); 287 if (key == KEYC_UNKNOWN) { 288 cmdq_error(item, "bad key: %s", value); 289 return (-1); 290 } 291 options_set_number(oo, oe->name, key); 292 return (0); 293 case OPTIONS_TABLE_COLOUR: 294 if ((number = colour_fromstring(value)) == -1) { 295 cmdq_error(item, "bad colour: %s", value); 296 return (-1); 297 } 298 o = options_set_number(oo, oe->name, number); 299 options_style_update_new(oo, o); 300 return (0); 301 case OPTIONS_TABLE_ATTRIBUTES: 302 if ((number = attributes_fromstring(value)) == -1) { 303 cmdq_error(item, "bad attributes: %s", value); 304 return (-1); 305 } 306 o = options_set_number(oo, oe->name, number); 307 options_style_update_new(oo, o); 308 return (0); 309 case OPTIONS_TABLE_FLAG: 310 return (cmd_set_option_flag(item, oe, oo, value)); 311 case OPTIONS_TABLE_CHOICE: 312 return (cmd_set_option_choice(item, oe, oo, value)); 313 case OPTIONS_TABLE_STYLE: 314 o = options_set_style(oo, oe->name, append, value); 315 if (o == NULL) { 316 cmdq_error(item, "bad style: %s", value); 317 return (-1); 318 } 319 options_style_update_old(oo, o); 320 return (0); 321 case OPTIONS_TABLE_ARRAY: 322 break; 323 } 324 return (-1); 325 } 326 327 static int 328 cmd_set_option_flag(struct cmdq_item *item, 329 const struct options_table_entry *oe, struct options *oo, 330 const char *value) 331 { 332 int flag; 333 334 if (value == NULL || *value == '\0') 335 flag = !options_get_number(oo, oe->name); 336 else if (strcmp(value, "1") == 0 || 337 strcasecmp(value, "on") == 0 || 338 strcasecmp(value, "yes") == 0) 339 flag = 1; 340 else if (strcmp(value, "0") == 0 || 341 strcasecmp(value, "off") == 0 || 342 strcasecmp(value, "no") == 0) 343 flag = 0; 344 else { 345 cmdq_error(item, "bad value: %s", value); 346 return (-1); 347 } 348 options_set_number(oo, oe->name, flag); 349 return (0); 350 } 351 352 static int 353 cmd_set_option_choice(struct cmdq_item *item, 354 const struct options_table_entry *oe, struct options *oo, 355 const char *value) 356 { 357 const char **cp; 358 int n, choice = -1; 359 360 if (value == NULL) { 361 choice = options_get_number(oo, oe->name); 362 if (choice < 2) 363 choice = !choice; 364 } else { 365 n = 0; 366 for (cp = oe->choices; *cp != NULL; cp++) { 367 if (strcmp(*cp, value) == 0) 368 choice = n; 369 n++; 370 } 371 if (choice == -1) { 372 cmdq_error(item, "unknown value: %s", value); 373 return (-1); 374 } 375 } 376 options_set_number(oo, oe->name, choice); 377 return (0); 378 } 379