xref: /openbsd/usr.bin/tmux/cmd-set-option.c (revision 94c0d63c)
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