1 /* $OpenBSD: cmd-set-option.c,v 1.110 2017/01/25 23:50:51 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 options_entry *, 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 int append = args_has(args, 'a'); 72 struct cmd_find_state *fs = &item->state.tflag; 73 struct session *s = fs->s; 74 struct winlink *wl = fs->wl; 75 struct window *w = wl->window; 76 struct client *c; 77 enum options_table_scope scope; 78 struct options *oo; 79 struct options_entry *parent, *o; 80 const char *name, *value, *target; 81 int window, idx, already, error, ambiguous; 82 char *cause; 83 84 /* Parse option name and index. */ 85 name = options_match(args->argv[0], &idx, &ambiguous); 86 if (name == NULL) { 87 if (args_has(args, 'q')) 88 return (CMD_RETURN_NORMAL); 89 if (ambiguous) 90 cmdq_error(item, "ambiguous option: %s", args->argv[0]); 91 else 92 cmdq_error(item, "invalid option: %s", args->argv[0]); 93 return (CMD_RETURN_ERROR); 94 } 95 if (args->argc < 2) 96 value = NULL; 97 else 98 value = args->argv[1]; 99 100 /* 101 * Figure out the scope: for user options it comes from the arguments, 102 * otherwise from the option name. 103 */ 104 if (*name == '@') { 105 window = (self->entry == &cmd_set_window_option_entry); 106 scope = options_scope_from_flags(args, window, fs, &oo, &cause); 107 } else { 108 if (options_get_only(global_options, name) != NULL) 109 scope = OPTIONS_TABLE_SERVER; 110 else if (options_get_only(global_s_options, name) != NULL) 111 scope = OPTIONS_TABLE_SESSION; 112 else if (options_get_only(global_w_options, name) != NULL) 113 scope = OPTIONS_TABLE_WINDOW; 114 else { 115 scope = OPTIONS_TABLE_NONE; 116 xasprintf(&cause, "unknown option: %s", args->argv[0]); 117 } 118 } 119 if (scope == OPTIONS_TABLE_NONE) { 120 if (args_has(args, 'q')) 121 return (CMD_RETURN_NORMAL); 122 cmdq_error(item, "%s", cause); 123 free(cause); 124 return (CMD_RETURN_ERROR); 125 } 126 127 /* Which table should this option go into? */ 128 if (scope == OPTIONS_TABLE_SERVER) 129 oo = global_options; 130 else if (scope == OPTIONS_TABLE_SESSION) { 131 if (args_has(self->args, 'g')) 132 oo = global_s_options; 133 else if (s == NULL) { 134 target = args_get(args, 't'); 135 if (target != NULL) 136 cmdq_error(item, "no such session: %s", target); 137 else 138 cmdq_error(item, "no current session"); 139 return (CMD_RETURN_ERROR); 140 } else 141 oo = s->options; 142 } else if (scope == OPTIONS_TABLE_WINDOW) { 143 if (args_has(self->args, 'g')) 144 oo = global_w_options; 145 else if (wl == NULL) { 146 target = args_get(args, 't'); 147 if (target != NULL) 148 cmdq_error(item, "no such window: %s", target); 149 else 150 cmdq_error(item, "no current window"); 151 return (CMD_RETURN_ERROR); 152 } else 153 oo = wl->window->options; 154 } 155 o = options_get_only(oo, name); 156 parent = options_get(oo, name); 157 158 /* Check that array options and indexes match up. */ 159 if (idx != -1) { 160 if (*name == '@' || options_array_size(parent, NULL) == -1) { 161 cmdq_error(item, "not 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, 0); 197 } else if (*name == '@') { 198 if (value == NULL) { 199 cmdq_error(item, "empty value"); 200 return (CMD_RETURN_ERROR); 201 } 202 options_set_string(oo, name, append, "%s", value); 203 } else if (idx == -1 && options_array_size(parent, NULL) == -1) { 204 error = cmd_set_option_set(self, item, oo, parent, value); 205 if (error != 0) 206 return (CMD_RETURN_ERROR); 207 } else { 208 if (value == NULL) { 209 cmdq_error(item, "empty value"); 210 return (CMD_RETURN_ERROR); 211 } 212 if (o == NULL) 213 o = options_empty(oo, options_table_entry(parent)); 214 if (idx == -1) { 215 if (!append) 216 options_array_clear(o); 217 options_array_assign(o, value); 218 } else if (options_array_set(o, idx, value, append) != 0) { 219 cmdq_error(item, "invalid index: %s", args->argv[0]); 220 return (CMD_RETURN_ERROR); 221 } 222 } 223 224 /* Update timers and so on for various options. */ 225 if (strcmp(name, "automatic-rename") == 0) { 226 RB_FOREACH(w, windows, &windows) { 227 if (w->active == NULL) 228 continue; 229 if (options_get_number(w->options, "automatic-rename")) 230 w->active->flags |= PANE_CHANGED; 231 } 232 } 233 if (strcmp(name, "key-table") == 0) { 234 TAILQ_FOREACH(c, &clients, entry) 235 server_client_set_key_table(c, NULL); 236 } 237 if (strcmp(name, "status") == 0 || 238 strcmp(name, "status-interval") == 0) 239 status_timer_start_all(); 240 if (strcmp(name, "monitor-silence") == 0) 241 alerts_reset_all(); 242 if (strcmp(name, "window-style") == 0 || 243 strcmp(name, "window-active-style") == 0) { 244 RB_FOREACH(w, windows, &windows) 245 w->flags |= WINDOW_STYLECHANGED; 246 } 247 if (strcmp(name, "pane-border-status") == 0) { 248 RB_FOREACH(w, windows, &windows) 249 layout_fix_panes(w, w->sx, w->sy); 250 } 251 252 /* 253 * Update sizes and redraw. May not always be necessary but do it 254 * anyway. 255 */ 256 recalculate_sizes(); 257 TAILQ_FOREACH(c, &clients, entry) { 258 if (c->session != NULL) 259 server_redraw_client(c); 260 } 261 262 return (CMD_RETURN_NORMAL); 263 } 264 265 static int 266 cmd_set_option_set(struct cmd *self, struct cmdq_item *item, struct options *oo, 267 struct options_entry *parent, const char *value) 268 { 269 const struct options_table_entry *oe; 270 struct args *args = self->args; 271 int append = args_has(args, 'a'); 272 struct options_entry *o; 273 long long number; 274 const char *errstr; 275 key_code key; 276 277 oe = options_table_entry(parent); 278 if (value == NULL && 279 oe->type != OPTIONS_TABLE_FLAG && 280 oe->type != OPTIONS_TABLE_CHOICE) { 281 cmdq_error(item, "empty value"); 282 return (-1); 283 } 284 285 switch (oe->type) { 286 case OPTIONS_TABLE_STRING: 287 options_set_string(oo, oe->name, append, "%s", value); 288 return (0); 289 case OPTIONS_TABLE_NUMBER: 290 number = strtonum(value, oe->minimum, oe->maximum, &errstr); 291 if (errstr != NULL) { 292 cmdq_error(item, "value is %s: %s", errstr, value); 293 return (-1); 294 } 295 options_set_number(oo, oe->name, number); 296 return (0); 297 case OPTIONS_TABLE_KEY: 298 key = key_string_lookup_string(value); 299 if (key == KEYC_UNKNOWN) { 300 cmdq_error(item, "bad key: %s", value); 301 return (-1); 302 } 303 options_set_number(oo, oe->name, key); 304 return (0); 305 case OPTIONS_TABLE_COLOUR: 306 if ((number = colour_fromstring(value)) == -1) { 307 cmdq_error(item, "bad colour: %s", value); 308 return (-1); 309 } 310 o = options_set_number(oo, oe->name, number); 311 options_style_update_new(oo, o); 312 return (0); 313 case OPTIONS_TABLE_ATTRIBUTES: 314 if ((number = attributes_fromstring(value)) == -1) { 315 cmdq_error(item, "bad attributes: %s", value); 316 return (-1); 317 } 318 o = options_set_number(oo, oe->name, number); 319 options_style_update_new(oo, o); 320 return (0); 321 case OPTIONS_TABLE_FLAG: 322 return (cmd_set_option_flag(item, oe, oo, value)); 323 case OPTIONS_TABLE_CHOICE: 324 return (cmd_set_option_choice(item, oe, oo, value)); 325 case OPTIONS_TABLE_STYLE: 326 o = options_set_style(oo, oe->name, append, value); 327 if (o == NULL) { 328 cmdq_error(item, "bad style: %s", value); 329 return (-1); 330 } 331 options_style_update_old(oo, o); 332 return (0); 333 case OPTIONS_TABLE_ARRAY: 334 break; 335 } 336 return (-1); 337 } 338 339 static int 340 cmd_set_option_flag(struct cmdq_item *item, 341 const struct options_table_entry *oe, struct options *oo, 342 const char *value) 343 { 344 int flag; 345 346 if (value == NULL || *value == '\0') 347 flag = !options_get_number(oo, oe->name); 348 else if (strcmp(value, "1") == 0 || 349 strcasecmp(value, "on") == 0 || 350 strcasecmp(value, "yes") == 0) 351 flag = 1; 352 else if (strcmp(value, "0") == 0 || 353 strcasecmp(value, "off") == 0 || 354 strcasecmp(value, "no") == 0) 355 flag = 0; 356 else { 357 cmdq_error(item, "bad value: %s", value); 358 return (-1); 359 } 360 options_set_number(oo, oe->name, flag); 361 return (0); 362 } 363 364 static int 365 cmd_set_option_choice(struct cmdq_item *item, 366 const struct options_table_entry *oe, struct options *oo, 367 const char *value) 368 { 369 const char **cp; 370 int n, choice = -1; 371 372 if (value == NULL) { 373 choice = options_get_number(oo, oe->name); 374 if (choice < 2) 375 choice = !choice; 376 } else { 377 n = 0; 378 for (cp = oe->choices; *cp != NULL; cp++) { 379 if (strcmp(*cp, value) == 0) 380 choice = n; 381 n++; 382 } 383 if (choice == -1) { 384 cmdq_error(item, "unknown value: %s", value); 385 return (-1); 386 } 387 } 388 options_set_number(oo, oe->name, choice); 389 return (0); 390 } 391