1 /* $OpenBSD: cmd-set-option.c,v 1.137 2020/06/16 08:18:34 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:uw", 1, 2 }, 37 .usage = "[-aFgopqsuw] " 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 options *oo; 78 struct options_entry *parent, *o; 79 char *name, *argument, *value = NULL, *cause; 80 int window, idx, already, error, ambiguous; 81 int scope; 82 83 window = (cmd_get_entry(self) == &cmd_set_window_option_entry); 84 85 /* Expand argument. */ 86 argument = format_single_from_target(item, args->argv[0]); 87 88 /* If set-hook -R, fire the hook straight away. */ 89 if (cmd_get_entry(self) == &cmd_set_hook_entry && args_has(args, 'R')) { 90 notify_hook(item, argument); 91 free(argument); 92 return (CMD_RETURN_NORMAL); 93 } 94 95 /* Parse option name and index. */ 96 name = options_match(argument, &idx, &ambiguous); 97 if (name == NULL) { 98 if (args_has(args, 'q')) 99 goto out; 100 if (ambiguous) 101 cmdq_error(item, "ambiguous option: %s", argument); 102 else 103 cmdq_error(item, "invalid option: %s", argument); 104 goto fail; 105 } 106 if (args->argc < 2) 107 value = NULL; 108 else if (args_has(args, 'F')) 109 value = format_single_from_target(item, args->argv[1]); 110 else 111 value = xstrdup(args->argv[1]); 112 113 /* Get the scope and table for the option .*/ 114 scope = options_scope_from_name(args, window, name, target, &oo, 115 &cause); 116 if (scope == OPTIONS_TABLE_NONE) { 117 if (args_has(args, 'q')) 118 goto out; 119 cmdq_error(item, "%s", cause); 120 free(cause); 121 goto fail; 122 } 123 o = options_get_only(oo, name); 124 parent = options_get(oo, name); 125 126 /* Check that array options and indexes match up. */ 127 if (idx != -1 && (*name == '@' || !options_is_array(parent))) { 128 cmdq_error(item, "not an array: %s", argument); 129 goto fail; 130 } 131 132 /* With -o, check this option is not already set. */ 133 if (!args_has(args, 'u') && args_has(args, 'o')) { 134 if (idx == -1) 135 already = (o != NULL); 136 else { 137 if (o == NULL) 138 already = 0; 139 else 140 already = (options_array_get(o, idx) != NULL); 141 } 142 if (already) { 143 if (args_has(args, 'q')) 144 goto out; 145 cmdq_error(item, "already set: %s", argument); 146 goto fail; 147 } 148 } 149 150 /* Change the option. */ 151 if (args_has(args, 'u')) { 152 if (o == NULL) 153 goto out; 154 if (options_remove_or_default(o, idx, &cause) != 0) { 155 cmdq_error(item, "%s", cause); 156 free(cause); 157 goto fail; 158 } 159 } else if (*name == '@') { 160 if (value == NULL) { 161 cmdq_error(item, "empty value"); 162 goto fail; 163 } 164 options_set_string(oo, name, append, "%s", value); 165 } else if (idx == -1 && !options_is_array(parent)) { 166 error = options_from_string(oo, options_table_entry(parent), 167 options_table_entry(parent)->name, value, 168 args_has(args, 'a'), &cause); 169 if (error != 0) { 170 cmdq_error(item, "%s", cause); 171 free(cause); 172 goto fail; 173 } 174 } else { 175 if (value == NULL) { 176 cmdq_error(item, "empty value"); 177 goto fail; 178 } 179 if (o == NULL) 180 o = options_empty(oo, options_table_entry(parent)); 181 if (idx == -1) { 182 if (!append) 183 options_array_clear(o); 184 if (options_array_assign(o, value, &cause) != 0) { 185 cmdq_error(item, "%s", cause); 186 free(cause); 187 goto fail; 188 } 189 } else if (options_array_set(o, idx, value, append, 190 &cause) != 0) { 191 cmdq_error(item, "%s", cause); 192 free(cause); 193 goto fail; 194 } 195 } 196 197 options_push_changes(name); 198 199 out: 200 free(argument); 201 free(value); 202 free(name); 203 return (CMD_RETURN_NORMAL); 204 205 fail: 206 free(argument); 207 free(value); 208 free(name); 209 return (CMD_RETURN_ERROR); 210 } 211