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