1 /* $OpenBSD: cmd-set-option.c,v 1.116 2017/05/31 17:56:48 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 fail; 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, "status") == 0 || 244 strcmp(name, "status-interval") == 0) 245 status_timer_start_all(); 246 if (strcmp(name, "monitor-silence") == 0) 247 alerts_reset_all(); 248 if (strcmp(name, "window-style") == 0 || 249 strcmp(name, "window-active-style") == 0) { 250 RB_FOREACH(w, windows, &windows) 251 w->flags |= WINDOW_STYLECHANGED; 252 } 253 if (strcmp(name, "pane-border-status") == 0) { 254 RB_FOREACH(w, windows, &windows) 255 layout_fix_panes(w, w->sx, w->sy); 256 } 257 RB_FOREACH(s, sessions, &sessions) 258 status_update_saved(s); 259 260 /* 261 * Update sizes and redraw. May not always be necessary but do it 262 * anyway. 263 */ 264 recalculate_sizes(); 265 TAILQ_FOREACH(loop, &clients, entry) { 266 if (loop->session != NULL) 267 server_redraw_client(loop); 268 } 269 270 out: 271 free(argument); 272 free(value); 273 free(name); 274 return (CMD_RETURN_NORMAL); 275 276 fail: 277 free(argument); 278 free(value); 279 free(name); 280 return (CMD_RETURN_ERROR); 281 } 282 283 static int 284 cmd_set_option_set(struct cmd *self, struct cmdq_item *item, struct options *oo, 285 struct options_entry *parent, const char *value) 286 { 287 const struct options_table_entry *oe; 288 struct args *args = self->args; 289 int append = args_has(args, 'a'); 290 struct options_entry *o; 291 long long number; 292 const char *errstr; 293 key_code key; 294 295 oe = options_table_entry(parent); 296 if (value == NULL && 297 oe->type != OPTIONS_TABLE_FLAG && 298 oe->type != OPTIONS_TABLE_CHOICE) { 299 cmdq_error(item, "empty value"); 300 return (-1); 301 } 302 303 switch (oe->type) { 304 case OPTIONS_TABLE_STRING: 305 options_set_string(oo, oe->name, append, "%s", value); 306 return (0); 307 case OPTIONS_TABLE_NUMBER: 308 number = strtonum(value, oe->minimum, oe->maximum, &errstr); 309 if (errstr != NULL) { 310 cmdq_error(item, "value is %s: %s", errstr, value); 311 return (-1); 312 } 313 options_set_number(oo, oe->name, number); 314 return (0); 315 case OPTIONS_TABLE_KEY: 316 key = key_string_lookup_string(value); 317 if (key == KEYC_UNKNOWN) { 318 cmdq_error(item, "bad key: %s", value); 319 return (-1); 320 } 321 options_set_number(oo, oe->name, key); 322 return (0); 323 case OPTIONS_TABLE_COLOUR: 324 if ((number = colour_fromstring(value)) == -1) { 325 cmdq_error(item, "bad colour: %s", value); 326 return (-1); 327 } 328 o = options_set_number(oo, oe->name, number); 329 options_style_update_new(oo, o); 330 return (0); 331 case OPTIONS_TABLE_ATTRIBUTES: 332 if ((number = attributes_fromstring(value)) == -1) { 333 cmdq_error(item, "bad attributes: %s", value); 334 return (-1); 335 } 336 o = options_set_number(oo, oe->name, number); 337 options_style_update_new(oo, o); 338 return (0); 339 case OPTIONS_TABLE_FLAG: 340 return (cmd_set_option_flag(item, oe, oo, value)); 341 case OPTIONS_TABLE_CHOICE: 342 return (cmd_set_option_choice(item, oe, oo, value)); 343 case OPTIONS_TABLE_STYLE: 344 o = options_set_style(oo, oe->name, append, value); 345 if (o == NULL) { 346 cmdq_error(item, "bad style: %s", value); 347 return (-1); 348 } 349 options_style_update_old(oo, o); 350 return (0); 351 case OPTIONS_TABLE_ARRAY: 352 break; 353 } 354 return (-1); 355 } 356 357 static int 358 cmd_set_option_flag(struct cmdq_item *item, 359 const struct options_table_entry *oe, struct options *oo, 360 const char *value) 361 { 362 int flag; 363 364 if (value == NULL || *value == '\0') 365 flag = !options_get_number(oo, oe->name); 366 else if (strcmp(value, "1") == 0 || 367 strcasecmp(value, "on") == 0 || 368 strcasecmp(value, "yes") == 0) 369 flag = 1; 370 else if (strcmp(value, "0") == 0 || 371 strcasecmp(value, "off") == 0 || 372 strcasecmp(value, "no") == 0) 373 flag = 0; 374 else { 375 cmdq_error(item, "bad value: %s", value); 376 return (-1); 377 } 378 options_set_number(oo, oe->name, flag); 379 return (0); 380 } 381 382 static int 383 cmd_set_option_choice(struct cmdq_item *item, 384 const struct options_table_entry *oe, struct options *oo, 385 const char *value) 386 { 387 const char **cp; 388 int n, choice = -1; 389 390 if (value == NULL) { 391 choice = options_get_number(oo, oe->name); 392 if (choice < 2) 393 choice = !choice; 394 } else { 395 n = 0; 396 for (cp = oe->choices; *cp != NULL; cp++) { 397 if (strcmp(*cp, value) == 0) 398 choice = n; 399 n++; 400 } 401 if (choice == -1) { 402 cmdq_error(item, "unknown value: %s", value); 403 return (-1); 404 } 405 } 406 options_set_number(oo, oe->name, choice); 407 return (0); 408 } 409