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