1 | /* $OpenBSD$ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2007 Nicholas Marriott <[email protected]> |
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 <fnmatch.h> |
22 | #include <stdlib.h> |
23 | #include <string.h> |
24 | |
25 | #include "tmux.h" |
26 | |
27 | /* |
28 | * Set an option. |
29 | */ |
30 | |
31 | static enum cmd_retval cmd_set_option_exec(struct cmd *, struct cmdq_item *); |
32 | |
33 | static int cmd_set_option_set(struct cmd *, struct cmdq_item *, |
34 | struct options *, struct options_entry *, const char *); |
35 | static int cmd_set_option_flag(struct cmdq_item *, |
36 | const struct options_table_entry *, struct options *, |
37 | const char *); |
38 | static int cmd_set_option_choice(struct cmdq_item *, |
39 | const struct options_table_entry *, struct options *, |
40 | const char *); |
41 | |
42 | const struct cmd_entry cmd_set_option_entry = { |
43 | .name = "set-option" , |
44 | .alias = "set" , |
45 | |
46 | .args = { "aFgopqst:uw" , 1, 2 }, |
47 | .usage = "[-aFgopqsuw] " CMD_TARGET_PANE_USAGE " option [value]" , |
48 | |
49 | .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, |
50 | |
51 | .flags = CMD_AFTERHOOK, |
52 | .exec = cmd_set_option_exec |
53 | }; |
54 | |
55 | const struct cmd_entry cmd_set_window_option_entry = { |
56 | .name = "set-window-option" , |
57 | .alias = "setw" , |
58 | |
59 | .args = { "aFgoqt:u" , 1, 2 }, |
60 | .usage = "[-aFgoqu] " CMD_TARGET_WINDOW_USAGE " option [value]" , |
61 | |
62 | .target = { 't', CMD_FIND_WINDOW, CMD_FIND_CANFAIL }, |
63 | |
64 | .flags = CMD_AFTERHOOK, |
65 | .exec = cmd_set_option_exec |
66 | }; |
67 | |
68 | const struct cmd_entry cmd_set_hook_entry = { |
69 | .name = "set-hook" , |
70 | .alias = NULL, |
71 | |
72 | .args = { "agRt:u" , 1, 2 }, |
73 | .usage = "[-agRu] " CMD_TARGET_SESSION_USAGE " hook [command]" , |
74 | |
75 | .target = { 't', CMD_FIND_SESSION, CMD_FIND_CANFAIL }, |
76 | |
77 | .flags = CMD_AFTERHOOK, |
78 | .exec = cmd_set_option_exec |
79 | }; |
80 | |
81 | static enum cmd_retval |
82 | cmd_set_option_exec(struct cmd *self, struct cmdq_item *item) |
83 | { |
84 | struct args *args = self->args; |
85 | int append = args_has(args, 'a'); |
86 | struct cmd_find_state *fs = &item->target; |
87 | struct client *c, *loop; |
88 | struct session *s = fs->s; |
89 | struct winlink *wl = fs->wl; |
90 | struct window *w; |
91 | struct window_pane *wp; |
92 | struct options *oo; |
93 | struct options_entry *parent, *o; |
94 | char *name, *argument, *value = NULL, *cause; |
95 | int window, idx, already, error, ambiguous; |
96 | int scope; |
97 | struct style *sy; |
98 | |
99 | window = (self->entry == &cmd_set_window_option_entry); |
100 | |
101 | /* Expand argument. */ |
102 | c = cmd_find_client(item, NULL, 1); |
103 | argument = format_single(item, args->argv[0], c, s, wl, NULL); |
104 | |
105 | /* If set-hook -R, fire the hook straight away. */ |
106 | if (self->entry == &cmd_set_hook_entry && args_has(args, 'R')) { |
107 | notify_hook(item, argument); |
108 | free(argument); |
109 | return (CMD_RETURN_NORMAL); |
110 | } |
111 | |
112 | /* Parse option name and index. */ |
113 | name = options_match(argument, &idx, &ambiguous); |
114 | if (name == NULL) { |
115 | if (args_has(args, 'q')) |
116 | goto out; |
117 | if (ambiguous) |
118 | cmdq_error(item, "ambiguous option: %s" , argument); |
119 | else |
120 | cmdq_error(item, "invalid option: %s" , argument); |
121 | goto fail; |
122 | } |
123 | if (args->argc < 2) |
124 | value = NULL; |
125 | else if (args_has(args, 'F')) |
126 | value = format_single(item, args->argv[1], c, s, wl, NULL); |
127 | else |
128 | value = xstrdup(args->argv[1]); |
129 | |
130 | /* Get the scope and table for the option .*/ |
131 | scope = options_scope_from_name(args, window, name, fs, &oo, &cause); |
132 | if (scope == OPTIONS_TABLE_NONE) { |
133 | if (args_has(args, 'q')) |
134 | goto out; |
135 | cmdq_error(item, "%s" , cause); |
136 | free(cause); |
137 | goto fail; |
138 | } |
139 | o = options_get_only(oo, name); |
140 | parent = options_get(oo, name); |
141 | |
142 | /* Check that array options and indexes match up. */ |
143 | if (idx != -1 && (*name == '@' || !options_isarray(parent))) { |
144 | cmdq_error(item, "not an array: %s" , argument); |
145 | goto fail; |
146 | } |
147 | |
148 | /* With -o, check this option is not already set. */ |
149 | if (!args_has(args, 'u') && args_has(args, 'o')) { |
150 | if (idx == -1) |
151 | already = (o != NULL); |
152 | else { |
153 | if (o == NULL) |
154 | already = 0; |
155 | else |
156 | already = (options_array_get(o, idx) != NULL); |
157 | } |
158 | if (already) { |
159 | if (args_has(args, 'q')) |
160 | goto out; |
161 | cmdq_error(item, "already set: %s" , argument); |
162 | goto fail; |
163 | } |
164 | } |
165 | |
166 | /* Change the option. */ |
167 | if (args_has(args, 'u')) { |
168 | if (o == NULL) |
169 | goto out; |
170 | if (idx == -1) { |
171 | if (*name == '@') |
172 | options_remove(o); |
173 | else if (oo == global_options || |
174 | oo == global_s_options || |
175 | oo == global_w_options) |
176 | options_default(oo, options_table_entry(o)); |
177 | else |
178 | options_remove(o); |
179 | } else if (options_array_set(o, idx, NULL, 0, &cause) != 0) { |
180 | cmdq_error(item, "%s" , cause); |
181 | free(cause); |
182 | goto fail; |
183 | } |
184 | } else if (*name == '@') { |
185 | if (value == NULL) { |
186 | cmdq_error(item, "empty value" ); |
187 | goto fail; |
188 | } |
189 | options_set_string(oo, name, append, "%s" , value); |
190 | } else if (idx == -1 && !options_isarray(parent)) { |
191 | error = cmd_set_option_set(self, item, oo, parent, value); |
192 | if (error != 0) |
193 | goto fail; |
194 | } else { |
195 | if (value == NULL) { |
196 | cmdq_error(item, "empty value" ); |
197 | goto fail; |
198 | } |
199 | if (o == NULL) |
200 | o = options_empty(oo, options_table_entry(parent)); |
201 | if (idx == -1) { |
202 | if (!append) |
203 | options_array_clear(o); |
204 | if (options_array_assign(o, value, &cause) != 0) { |
205 | cmdq_error(item, "%s" , cause); |
206 | free(cause); |
207 | goto fail; |
208 | } |
209 | } else if (options_array_set(o, idx, value, append, |
210 | &cause) != 0) { |
211 | cmdq_error(item, "%s" , cause); |
212 | free(cause); |
213 | goto fail; |
214 | } |
215 | } |
216 | |
217 | /* Update timers and so on for various options. */ |
218 | if (strcmp(name, "automatic-rename" ) == 0) { |
219 | RB_FOREACH(w, windows, &windows) { |
220 | if (w->active == NULL) |
221 | continue; |
222 | if (options_get_number(w->options, "automatic-rename" )) |
223 | w->active->flags |= PANE_CHANGED; |
224 | } |
225 | } |
226 | if (strcmp(name, "key-table" ) == 0) { |
227 | TAILQ_FOREACH(loop, &clients, entry) |
228 | server_client_set_key_table(loop, NULL); |
229 | } |
230 | if (strcmp(name, "user-keys" ) == 0) { |
231 | TAILQ_FOREACH(loop, &clients, entry) { |
232 | if (loop->tty.flags & TTY_OPENED) |
233 | tty_keys_build(&loop->tty); |
234 | } |
235 | } |
236 | if (strcmp(name, "status-fg" ) == 0 || strcmp(name, "status-bg" ) == 0) { |
237 | sy = options_get_style(oo, "status-style" ); |
238 | sy->gc.fg = options_get_number(oo, "status-fg" ); |
239 | sy->gc.bg = options_get_number(oo, "status-bg" ); |
240 | } |
241 | if (strcmp(name, "status-style" ) == 0) { |
242 | sy = options_get_style(oo, "status-style" ); |
243 | options_set_number(oo, "status-fg" , sy->gc.fg); |
244 | options_set_number(oo, "status-bg" , sy->gc.bg); |
245 | } |
246 | if (strcmp(name, "status" ) == 0 || |
247 | strcmp(name, "status-interval" ) == 0) |
248 | status_timer_start_all(); |
249 | if (strcmp(name, "monitor-silence" ) == 0) |
250 | alerts_reset_all(); |
251 | if (strcmp(name, "window-style" ) == 0 || |
252 | strcmp(name, "window-active-style" ) == 0) { |
253 | RB_FOREACH(wp, window_pane_tree, &all_window_panes) |
254 | wp->flags |= PANE_STYLECHANGED; |
255 | } |
256 | if (strcmp(name, "pane-border-status" ) == 0) { |
257 | RB_FOREACH(w, windows, &windows) |
258 | layout_fix_panes(w); |
259 | } |
260 | RB_FOREACH(s, sessions, &sessions) |
261 | status_update_cache(s); |
262 | |
263 | /* |
264 | * Update sizes and redraw. May not always be necessary but do it |
265 | * anyway. |
266 | */ |
267 | recalculate_sizes(); |
268 | TAILQ_FOREACH(loop, &clients, entry) { |
269 | if (loop->session != NULL) |
270 | server_redraw_client(loop); |
271 | } |
272 | |
273 | out: |
274 | free(argument); |
275 | free(value); |
276 | free(name); |
277 | return (CMD_RETURN_NORMAL); |
278 | |
279 | fail: |
280 | free(argument); |
281 | free(value); |
282 | free(name); |
283 | return (CMD_RETURN_ERROR); |
284 | } |
285 | |
286 | static int |
287 | cmd_set_option_set(struct cmd *self, struct cmdq_item *item, struct options *oo, |
288 | struct options_entry *parent, const char *value) |
289 | { |
290 | const struct options_table_entry *oe; |
291 | struct args *args = self->args; |
292 | int append = args_has(args, 'a'); |
293 | struct options_entry *o; |
294 | long long number; |
295 | const char *errstr, *new; |
296 | char *old; |
297 | key_code key; |
298 | |
299 | oe = options_table_entry(parent); |
300 | if (value == NULL && |
301 | oe->type != OPTIONS_TABLE_FLAG && |
302 | oe->type != OPTIONS_TABLE_CHOICE) { |
303 | cmdq_error(item, "empty value" ); |
304 | return (-1); |
305 | } |
306 | |
307 | switch (oe->type) { |
308 | case OPTIONS_TABLE_STRING: |
309 | old = xstrdup(options_get_string(oo, oe->name)); |
310 | options_set_string(oo, oe->name, append, "%s" , value); |
311 | new = options_get_string(oo, oe->name); |
312 | if (oe->pattern != NULL && fnmatch(oe->pattern, new, 0) != 0) { |
313 | options_set_string(oo, oe->name, 0, "%s" , old); |
314 | free(old); |
315 | cmdq_error(item, "value is invalid: %s" , value); |
316 | return (-1); |
317 | } |
318 | free(old); |
319 | return (0); |
320 | case OPTIONS_TABLE_NUMBER: |
321 | number = strtonum(value, oe->minimum, oe->maximum, &errstr); |
322 | if (errstr != NULL) { |
323 | cmdq_error(item, "value is %s: %s" , errstr, value); |
324 | return (-1); |
325 | } |
326 | options_set_number(oo, oe->name, number); |
327 | return (0); |
328 | case OPTIONS_TABLE_KEY: |
329 | key = key_string_lookup_string(value); |
330 | if (key == KEYC_UNKNOWN) { |
331 | cmdq_error(item, "bad key: %s" , value); |
332 | return (-1); |
333 | } |
334 | options_set_number(oo, oe->name, key); |
335 | return (0); |
336 | case OPTIONS_TABLE_COLOUR: |
337 | if ((number = colour_fromstring(value)) == -1) { |
338 | cmdq_error(item, "bad colour: %s" , value); |
339 | return (-1); |
340 | } |
341 | options_set_number(oo, oe->name, number); |
342 | return (0); |
343 | case OPTIONS_TABLE_FLAG: |
344 | return (cmd_set_option_flag(item, oe, oo, value)); |
345 | case OPTIONS_TABLE_CHOICE: |
346 | return (cmd_set_option_choice(item, oe, oo, value)); |
347 | case OPTIONS_TABLE_STYLE: |
348 | o = options_set_style(oo, oe->name, append, value); |
349 | if (o == NULL) { |
350 | cmdq_error(item, "bad style: %s" , value); |
351 | return (-1); |
352 | } |
353 | return (0); |
354 | case OPTIONS_TABLE_COMMAND: |
355 | break; |
356 | } |
357 | return (-1); |
358 | } |
359 | |
360 | static int |
361 | cmd_set_option_flag(struct cmdq_item *item, |
362 | const struct options_table_entry *oe, struct options *oo, |
363 | const char *value) |
364 | { |
365 | int flag; |
366 | |
367 | if (value == NULL || *value == '\0') |
368 | flag = !options_get_number(oo, oe->name); |
369 | else if (strcmp(value, "1" ) == 0 || |
370 | strcasecmp(value, "on" ) == 0 || |
371 | strcasecmp(value, "yes" ) == 0) |
372 | flag = 1; |
373 | else if (strcmp(value, "0" ) == 0 || |
374 | strcasecmp(value, "off" ) == 0 || |
375 | strcasecmp(value, "no" ) == 0) |
376 | flag = 0; |
377 | else { |
378 | cmdq_error(item, "bad value: %s" , value); |
379 | return (-1); |
380 | } |
381 | options_set_number(oo, oe->name, flag); |
382 | return (0); |
383 | } |
384 | |
385 | static int |
386 | cmd_set_option_choice(struct cmdq_item *item, |
387 | const struct options_table_entry *oe, struct options *oo, |
388 | const char *value) |
389 | { |
390 | const char **cp; |
391 | int n, choice = -1; |
392 | |
393 | if (value == NULL) { |
394 | choice = options_get_number(oo, oe->name); |
395 | if (choice < 2) |
396 | choice = !choice; |
397 | } else { |
398 | n = 0; |
399 | for (cp = oe->choices; *cp != NULL; cp++) { |
400 | if (strcmp(*cp, value) == 0) |
401 | choice = n; |
402 | n++; |
403 | } |
404 | if (choice == -1) { |
405 | cmdq_error(item, "unknown value: %s" , value); |
406 | return (-1); |
407 | } |
408 | } |
409 | options_set_number(oo, oe->name, choice); |
410 | return (0); |
411 | } |
412 | |