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