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