1 /* $OpenBSD: cmd-set-option.c,v 1.135 2020/05/16 16:02:24 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 const struct cmd_entry cmd_set_option_entry = { 34 .name = "set-option", 35 .alias = "set", 36 37 .args = { "aFgopqst:uw", 1, 2 }, 38 .usage = "[-aFgopqsuw] " CMD_TARGET_PANE_USAGE " option [value]", 39 40 .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, 41 42 .flags = CMD_AFTERHOOK, 43 .exec = cmd_set_option_exec 44 }; 45 46 const struct cmd_entry cmd_set_window_option_entry = { 47 .name = "set-window-option", 48 .alias = "setw", 49 50 .args = { "aFgoqt:u", 1, 2 }, 51 .usage = "[-aFgoqu] " CMD_TARGET_WINDOW_USAGE " option [value]", 52 53 .target = { 't', CMD_FIND_WINDOW, CMD_FIND_CANFAIL }, 54 55 .flags = CMD_AFTERHOOK, 56 .exec = cmd_set_option_exec 57 }; 58 59 const struct cmd_entry cmd_set_hook_entry = { 60 .name = "set-hook", 61 .alias = NULL, 62 63 .args = { "agpRt:uw", 1, 2 }, 64 .usage = "[-agpRuw] " CMD_TARGET_PANE_USAGE " hook [command]", 65 66 .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, 67 68 .flags = CMD_AFTERHOOK, 69 .exec = cmd_set_option_exec 70 }; 71 72 static enum cmd_retval 73 cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) 74 { 75 struct args *args = cmd_get_args(self); 76 int append = args_has(args, 'a'); 77 struct cmd_find_state *target = cmdq_get_target(item); 78 struct options *oo; 79 struct options_entry *parent, *o; 80 char *name, *argument, *value = NULL, *cause; 81 int window, idx, already, error, ambiguous; 82 int scope; 83 84 window = (cmd_get_entry(self) == &cmd_set_window_option_entry); 85 86 /* Expand argument. */ 87 argument = format_single_from_target(item, args->argv[0]); 88 89 /* If set-hook -R, fire the hook straight away. */ 90 if (cmd_get_entry(self) == &cmd_set_hook_entry && args_has(args, 'R')) { 91 notify_hook(item, argument); 92 free(argument); 93 return (CMD_RETURN_NORMAL); 94 } 95 96 /* Parse option name and index. */ 97 name = options_match(argument, &idx, &ambiguous); 98 if (name == NULL) { 99 if (args_has(args, 'q')) 100 goto out; 101 if (ambiguous) 102 cmdq_error(item, "ambiguous option: %s", argument); 103 else 104 cmdq_error(item, "invalid option: %s", argument); 105 goto fail; 106 } 107 if (args->argc < 2) 108 value = NULL; 109 else if (args_has(args, 'F')) 110 value = format_single_from_target(item, args->argv[1]); 111 else 112 value = xstrdup(args->argv[1]); 113 114 /* Get the scope and table for the option .*/ 115 scope = options_scope_from_name(args, window, name, target, &oo, 116 &cause); 117 if (scope == OPTIONS_TABLE_NONE) { 118 if (args_has(args, 'q')) 119 goto out; 120 cmdq_error(item, "%s", cause); 121 free(cause); 122 goto fail; 123 } 124 o = options_get_only(oo, name); 125 parent = options_get(oo, name); 126 127 /* Check that array options and indexes match up. */ 128 if (idx != -1 && (*name == '@' || !options_is_array(parent))) { 129 cmdq_error(item, "not an array: %s", argument); 130 goto fail; 131 } 132 133 /* With -o, check this option is not already set. */ 134 if (!args_has(args, 'u') && args_has(args, 'o')) { 135 if (idx == -1) 136 already = (o != NULL); 137 else { 138 if (o == NULL) 139 already = 0; 140 else 141 already = (options_array_get(o, idx) != NULL); 142 } 143 if (already) { 144 if (args_has(args, 'q')) 145 goto out; 146 cmdq_error(item, "already set: %s", argument); 147 goto fail; 148 } 149 } 150 151 /* Change the option. */ 152 if (args_has(args, 'u')) { 153 if (o == NULL) 154 goto out; 155 if (idx == -1) { 156 if (*name == '@') 157 options_remove(o); 158 else if (oo == global_options || 159 oo == global_s_options || 160 oo == global_w_options) 161 options_default(oo, options_table_entry(o)); 162 else 163 options_remove(o); 164 } else if (options_array_set(o, idx, NULL, 0, &cause) != 0) { 165 cmdq_error(item, "%s", cause); 166 free(cause); 167 goto fail; 168 } 169 } else if (*name == '@') { 170 if (value == NULL) { 171 cmdq_error(item, "empty value"); 172 goto fail; 173 } 174 options_set_string(oo, name, append, "%s", value); 175 } else if (idx == -1 && !options_is_array(parent)) { 176 error = options_from_string(oo, options_table_entry(parent), 177 options_table_entry(parent)->name, value, 178 args_has(args, 'a'), &cause); 179 if (error != 0) { 180 cmdq_error(item, "%s", cause); 181 free(cause); 182 goto fail; 183 } 184 } else { 185 if (value == NULL) { 186 cmdq_error(item, "empty value"); 187 goto fail; 188 } 189 if (o == NULL) 190 o = options_empty(oo, options_table_entry(parent)); 191 if (idx == -1) { 192 if (!append) 193 options_array_clear(o); 194 if (options_array_assign(o, value, &cause) != 0) { 195 cmdq_error(item, "%s", cause); 196 free(cause); 197 goto fail; 198 } 199 } else if (options_array_set(o, idx, value, append, 200 &cause) != 0) { 201 cmdq_error(item, "%s", cause); 202 free(cause); 203 goto fail; 204 } 205 } 206 207 options_push_changes(name); 208 209 out: 210 free(argument); 211 free(value); 212 free(name); 213 return (CMD_RETURN_NORMAL); 214 215 fail: 216 free(argument); 217 free(value); 218 free(name); 219 return (CMD_RETURN_ERROR); 220 } 221