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