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