1 /* $OpenBSD: cmd-set-option.c,v 1.133 2020/04/13 20:54:15 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 = { "aFgopqst:uw", 1, 2 }, 47 .usage = "[-aFgopqsuw] " CMD_TARGET_PANE_USAGE " option [value]", 48 49 .target = { 't', CMD_FIND_PANE, 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 const struct cmd_entry cmd_set_hook_entry = { 69 .name = "set-hook", 70 .alias = NULL, 71 72 .args = { "agpRt:uw", 1, 2 }, 73 .usage = "[-agpRuw] " CMD_TARGET_PANE_USAGE " hook [command]", 74 75 .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, 76 77 .flags = CMD_AFTERHOOK, 78 .exec = cmd_set_option_exec 79 }; 80 81 static enum cmd_retval 82 cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) 83 { 84 struct args *args = cmd_get_args(self); 85 int append = args_has(args, 'a'); 86 struct cmd_find_state *target = cmdq_get_target(item); 87 struct client *loop; 88 struct session *s = target->s; 89 struct window *w; 90 struct window_pane *wp; 91 struct options *oo; 92 struct options_entry *parent, *o; 93 char *name, *argument, *value = NULL, *cause; 94 int window, idx, already, error, ambiguous; 95 int scope; 96 struct style *sy; 97 98 window = (cmd_get_entry(self) == &cmd_set_window_option_entry); 99 100 /* Expand argument. */ 101 argument = format_single_from_target(item, args->argv[0]); 102 103 /* If set-hook -R, fire the hook straight away. */ 104 if (cmd_get_entry(self) == &cmd_set_hook_entry && args_has(args, 'R')) { 105 notify_hook(item, argument); 106 free(argument); 107 return (CMD_RETURN_NORMAL); 108 } 109 110 /* Parse option name and index. */ 111 name = options_match(argument, &idx, &ambiguous); 112 if (name == NULL) { 113 if (args_has(args, 'q')) 114 goto out; 115 if (ambiguous) 116 cmdq_error(item, "ambiguous option: %s", argument); 117 else 118 cmdq_error(item, "invalid option: %s", argument); 119 goto fail; 120 } 121 if (args->argc < 2) 122 value = NULL; 123 else if (args_has(args, 'F')) 124 value = format_single_from_target(item, args->argv[1]); 125 else 126 value = xstrdup(args->argv[1]); 127 128 /* Get the scope and table for the option .*/ 129 scope = options_scope_from_name(args, window, name, target, &oo, 130 &cause); 131 if (scope == OPTIONS_TABLE_NONE) { 132 if (args_has(args, 'q')) 133 goto out; 134 cmdq_error(item, "%s", cause); 135 free(cause); 136 goto fail; 137 } 138 o = options_get_only(oo, name); 139 parent = options_get(oo, name); 140 141 /* Check that array options and indexes match up. */ 142 if (idx != -1 && (*name == '@' || !options_isarray(parent))) { 143 cmdq_error(item, "not an array: %s", argument); 144 goto fail; 145 } 146 147 /* With -o, check this option is not already set. */ 148 if (!args_has(args, 'u') && args_has(args, 'o')) { 149 if (idx == -1) 150 already = (o != NULL); 151 else { 152 if (o == NULL) 153 already = 0; 154 else 155 already = (options_array_get(o, idx) != NULL); 156 } 157 if (already) { 158 if (args_has(args, 'q')) 159 goto out; 160 cmdq_error(item, "already set: %s", argument); 161 goto fail; 162 } 163 } 164 165 /* Change the option. */ 166 if (args_has(args, 'u')) { 167 if (o == NULL) 168 goto out; 169 if (idx == -1) { 170 if (*name == '@') 171 options_remove(o); 172 else if (oo == global_options || 173 oo == global_s_options || 174 oo == global_w_options) 175 options_default(oo, options_table_entry(o)); 176 else 177 options_remove(o); 178 } else if (options_array_set(o, idx, NULL, 0, &cause) != 0) { 179 cmdq_error(item, "%s", cause); 180 free(cause); 181 goto fail; 182 } 183 } else if (*name == '@') { 184 if (value == NULL) { 185 cmdq_error(item, "empty value"); 186 goto fail; 187 } 188 options_set_string(oo, name, append, "%s", value); 189 } else if (idx == -1 && !options_isarray(parent)) { 190 error = cmd_set_option_set(self, item, oo, parent, value); 191 if (error != 0) 192 goto fail; 193 } else { 194 if (value == NULL) { 195 cmdq_error(item, "empty value"); 196 goto fail; 197 } 198 if (o == NULL) 199 o = options_empty(oo, options_table_entry(parent)); 200 if (idx == -1) { 201 if (!append) 202 options_array_clear(o); 203 if (options_array_assign(o, value, &cause) != 0) { 204 cmdq_error(item, "%s", cause); 205 free(cause); 206 goto fail; 207 } 208 } else if (options_array_set(o, idx, value, append, 209 &cause) != 0) { 210 cmdq_error(item, "%s", cause); 211 free(cause); 212 goto fail; 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(loop, &clients, entry) 227 server_client_set_key_table(loop, NULL); 228 } 229 if (strcmp(name, "user-keys") == 0) { 230 TAILQ_FOREACH(loop, &clients, entry) { 231 if (loop->tty.flags & TTY_OPENED) 232 tty_keys_build(&loop->tty); 233 } 234 } 235 if (strcmp(name, "status-fg") == 0 || strcmp(name, "status-bg") == 0) { 236 sy = options_get_style(oo, "status-style"); 237 sy->gc.fg = options_get_number(oo, "status-fg"); 238 sy->gc.bg = options_get_number(oo, "status-bg"); 239 } 240 if (strcmp(name, "status-style") == 0) { 241 sy = options_get_style(oo, "status-style"); 242 options_set_number(oo, "status-fg", sy->gc.fg); 243 options_set_number(oo, "status-bg", sy->gc.bg); 244 } 245 if (strcmp(name, "status") == 0 || 246 strcmp(name, "status-interval") == 0) 247 status_timer_start_all(); 248 if (strcmp(name, "monitor-silence") == 0) 249 alerts_reset_all(); 250 if (strcmp(name, "window-style") == 0 || 251 strcmp(name, "window-active-style") == 0) { 252 RB_FOREACH(wp, window_pane_tree, &all_window_panes) 253 wp->flags |= PANE_STYLECHANGED; 254 } 255 if (strcmp(name, "pane-border-status") == 0) { 256 RB_FOREACH(w, windows, &windows) 257 layout_fix_panes(w); 258 } 259 RB_FOREACH(s, sessions, &sessions) 260 status_update_cache(s); 261 262 /* 263 * Update sizes and redraw. May not always be necessary but do it 264 * anyway. 265 */ 266 recalculate_sizes(); 267 TAILQ_FOREACH(loop, &clients, entry) { 268 if (loop->session != NULL) 269 server_redraw_client(loop); 270 } 271 272 out: 273 free(argument); 274 free(value); 275 free(name); 276 return (CMD_RETURN_NORMAL); 277 278 fail: 279 free(argument); 280 free(value); 281 free(name); 282 return (CMD_RETURN_ERROR); 283 } 284 285 static int 286 cmd_set_option_set(struct cmd *self, struct cmdq_item *item, struct options *oo, 287 struct options_entry *parent, const char *value) 288 { 289 const struct options_table_entry *oe; 290 struct args *args = cmd_get_args(self); 291 int append = args_has(args, 'a'); 292 struct options_entry *o; 293 long long number; 294 const char *errstr, *new; 295 char *old; 296 key_code key; 297 298 oe = options_table_entry(parent); 299 if (value == NULL && 300 oe->type != OPTIONS_TABLE_FLAG && 301 oe->type != OPTIONS_TABLE_CHOICE) { 302 cmdq_error(item, "empty value"); 303 return (-1); 304 } 305 306 switch (oe->type) { 307 case OPTIONS_TABLE_STRING: 308 old = xstrdup(options_get_string(oo, oe->name)); 309 options_set_string(oo, oe->name, append, "%s", value); 310 new = options_get_string(oo, oe->name); 311 if (strcmp(oe->name, "default-shell") == 0 && 312 !checkshell(new)) { 313 options_set_string(oo, oe->name, 0, "%s", old); 314 free(old); 315 cmdq_error(item, "not a suitable shell: %s", value); 316 return (-1); 317 } 318 if (oe->pattern != NULL && fnmatch(oe->pattern, new, 0) != 0) { 319 options_set_string(oo, oe->name, 0, "%s", old); 320 free(old); 321 cmdq_error(item, "value is invalid: %s", value); 322 return (-1); 323 } 324 free(old); 325 return (0); 326 case OPTIONS_TABLE_NUMBER: 327 number = strtonum(value, oe->minimum, oe->maximum, &errstr); 328 if (errstr != NULL) { 329 cmdq_error(item, "value is %s: %s", errstr, value); 330 return (-1); 331 } 332 options_set_number(oo, oe->name, number); 333 return (0); 334 case OPTIONS_TABLE_KEY: 335 key = key_string_lookup_string(value); 336 if (key == KEYC_UNKNOWN) { 337 cmdq_error(item, "bad key: %s", value); 338 return (-1); 339 } 340 options_set_number(oo, oe->name, key); 341 return (0); 342 case OPTIONS_TABLE_COLOUR: 343 if ((number = colour_fromstring(value)) == -1) { 344 cmdq_error(item, "bad colour: %s", value); 345 return (-1); 346 } 347 options_set_number(oo, oe->name, number); 348 return (0); 349 case OPTIONS_TABLE_FLAG: 350 return (cmd_set_option_flag(item, oe, oo, value)); 351 case OPTIONS_TABLE_CHOICE: 352 return (cmd_set_option_choice(item, oe, oo, value)); 353 case OPTIONS_TABLE_STYLE: 354 o = options_set_style(oo, oe->name, append, value); 355 if (o == NULL) { 356 cmdq_error(item, "bad style: %s", value); 357 return (-1); 358 } 359 return (0); 360 case OPTIONS_TABLE_COMMAND: 361 break; 362 } 363 return (-1); 364 } 365 366 static int 367 cmd_set_option_flag(struct cmdq_item *item, 368 const struct options_table_entry *oe, struct options *oo, 369 const char *value) 370 { 371 int flag; 372 373 if (value == NULL || *value == '\0') 374 flag = !options_get_number(oo, oe->name); 375 else if (strcmp(value, "1") == 0 || 376 strcasecmp(value, "on") == 0 || 377 strcasecmp(value, "yes") == 0) 378 flag = 1; 379 else if (strcmp(value, "0") == 0 || 380 strcasecmp(value, "off") == 0 || 381 strcasecmp(value, "no") == 0) 382 flag = 0; 383 else { 384 cmdq_error(item, "bad value: %s", value); 385 return (-1); 386 } 387 options_set_number(oo, oe->name, flag); 388 return (0); 389 } 390 391 static int 392 cmd_set_option_choice(struct cmdq_item *item, 393 const struct options_table_entry *oe, struct options *oo, 394 const char *value) 395 { 396 const char **cp; 397 int n, choice = -1; 398 399 if (value == NULL) { 400 choice = options_get_number(oo, oe->name); 401 if (choice < 2) 402 choice = !choice; 403 } else { 404 n = 0; 405 for (cp = oe->choices; *cp != NULL; cp++) { 406 if (strcmp(*cp, value) == 0) 407 choice = n; 408 n++; 409 } 410 if (choice == -1) { 411 cmdq_error(item, "unknown value: %s", value); 412 return (-1); 413 } 414 } 415 options_set_number(oo, oe->name, choice); 416 return (0); 417 } 418