1 /* $OpenBSD: cmd-set-option.c,v 1.138 2020/12/15 08:31:50 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, *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') && scope == OPTIONS_TABLE_WINDOW) { 153 TAILQ_FOREACH(loop, &target->w->panes, entry) { 154 po = options_get_only(loop->options, name); 155 if (po == NULL) 156 continue; 157 if (options_remove_or_default(po, idx, &cause) != 0) { 158 cmdq_error(item, "%s", cause); 159 free(cause); 160 goto fail; 161 } 162 } 163 } 164 if (args_has(args, 'u') || args_has(args, 'U')) { 165 if (o == NULL) 166 goto out; 167 if (options_remove_or_default(o, idx, &cause) != 0) { 168 cmdq_error(item, "%s", cause); 169 free(cause); 170 goto fail; 171 } 172 } else if (*name == '@') { 173 if (value == NULL) { 174 cmdq_error(item, "empty value"); 175 goto fail; 176 } 177 options_set_string(oo, name, append, "%s", value); 178 } else if (idx == -1 && !options_is_array(parent)) { 179 error = options_from_string(oo, options_table_entry(parent), 180 options_table_entry(parent)->name, value, 181 args_has(args, 'a'), &cause); 182 if (error != 0) { 183 cmdq_error(item, "%s", cause); 184 free(cause); 185 goto fail; 186 } 187 } else { 188 if (value == NULL) { 189 cmdq_error(item, "empty value"); 190 goto fail; 191 } 192 if (o == NULL) 193 o = options_empty(oo, options_table_entry(parent)); 194 if (idx == -1) { 195 if (!append) 196 options_array_clear(o); 197 if (options_array_assign(o, value, &cause) != 0) { 198 cmdq_error(item, "%s", cause); 199 free(cause); 200 goto fail; 201 } 202 } else if (options_array_set(o, idx, value, append, 203 &cause) != 0) { 204 cmdq_error(item, "%s", cause); 205 free(cause); 206 goto fail; 207 } 208 } 209 210 options_push_changes(name); 211 212 out: 213 free(argument); 214 free(value); 215 free(name); 216 return (CMD_RETURN_NORMAL); 217 218 fail: 219 free(argument); 220 free(value); 221 free(name); 222 return (CMD_RETURN_ERROR); 223 } 224