xref: /openbsd-src/usr.bin/tmux/cmd-set-option.c (revision 6f05df2d9be0954bec42d51d943d77bd250fb664)
1 /* $OpenBSD: cmd-set-option.c,v 1.71 2014/10/20 22:29:25 nicm Exp $ */
2 
3 /*
4  * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
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 enum cmd_retval	cmd_set_option_exec(struct cmd *, struct cmd_q *);
31 
32 enum cmd_retval	cmd_set_option_user(struct cmd *, struct cmd_q *,
33 	    const char *, const char *);
34 
35 int	cmd_set_option_unset(struct cmd *, struct cmd_q *,
36 	    const struct options_table_entry *, struct options *,
37 	    const char *);
38 int	cmd_set_option_set(struct cmd *, struct cmd_q *,
39 	    const struct options_table_entry *, struct options *,
40 	    const char *);
41 
42 struct options_entry *cmd_set_option_string(struct cmd *, struct cmd_q *,
43 	    const struct options_table_entry *, struct options *,
44 	    const char *);
45 struct options_entry *cmd_set_option_number(struct cmd *, struct cmd_q *,
46 	    const struct options_table_entry *, struct options *,
47 	    const char *);
48 struct options_entry *cmd_set_option_key(struct cmd *, struct cmd_q *,
49 	    const struct options_table_entry *, struct options *,
50 	    const char *);
51 struct options_entry *cmd_set_option_colour(struct cmd *, struct cmd_q *,
52 	    const struct options_table_entry *, struct options *,
53 	    const char *);
54 struct options_entry *cmd_set_option_attributes(struct cmd *, struct cmd_q *,
55 	    const struct options_table_entry *, struct options *,
56 	    const char *);
57 struct options_entry *cmd_set_option_flag(struct cmd *, struct cmd_q *,
58 	    const struct options_table_entry *, struct options *,
59 	    const char *);
60 struct options_entry *cmd_set_option_choice(struct cmd *, struct cmd_q *,
61 	    const struct options_table_entry *, struct options *,
62 	    const char *);
63 struct options_entry *cmd_set_option_style(struct cmd *, struct cmd_q *,
64 	    const struct options_table_entry *, struct options *,
65 	    const char *);
66 
67 const struct cmd_entry cmd_set_option_entry = {
68 	"set-option", "set",
69 	"agoqst:uw", 1, 2,
70 	"[-agosquw] [-t target-session|target-window] option [value]",
71 	0,
72 	cmd_set_option_exec
73 };
74 
75 const struct cmd_entry cmd_set_window_option_entry = {
76 	"set-window-option", "setw",
77 	"agoqt:u", 1, 2,
78 	"[-agoqu] " CMD_TARGET_WINDOW_USAGE " option [value]",
79 	0,
80 	cmd_set_option_exec
81 };
82 
83 enum cmd_retval
84 cmd_set_option_exec(struct cmd *self, struct cmd_q *cmdq)
85 {
86 	struct args				*args = self->args;
87 	const struct options_table_entry	*table, *oe;
88 	struct session				*s;
89 	struct winlink				*wl;
90 	struct client				*c;
91 	struct options				*oo;
92 	struct window				*w;
93 	const char				*optstr, *valstr;
94 	u_int					 i;
95 
96 	/* Get the option name and value. */
97 	optstr = args->argv[0];
98 	if (*optstr == '\0') {
99 		cmdq_error(cmdq, "invalid option");
100 		return (CMD_RETURN_ERROR);
101 	}
102 	if (args->argc < 2)
103 		valstr = NULL;
104 	else
105 		valstr = args->argv[1];
106 
107 	/* Is this a user option? */
108 	if (*optstr == '@')
109 		return (cmd_set_option_user(self, cmdq, optstr, valstr));
110 
111 	/* Find the option entry, try each table. */
112 	table = oe = NULL;
113 	if (options_table_find(optstr, &table, &oe) != 0) {
114 		cmdq_error(cmdq, "ambiguous option: %s", optstr);
115 		return (CMD_RETURN_ERROR);
116 	}
117 	if (oe == NULL) {
118 		if (!args_has(args, 'q')) {
119 			cmdq_error(cmdq, "unknown option: %s", optstr);
120 			return (CMD_RETURN_ERROR);
121 		}
122 		return (CMD_RETURN_NORMAL);
123 	}
124 
125 	/* Work out the tree from the table. */
126 	if (table == server_options_table)
127 		oo = &global_options;
128 	else if (table == window_options_table) {
129 		if (args_has(self->args, 'g'))
130 			oo = &global_w_options;
131 		else {
132 			wl = cmd_find_window(cmdq, args_get(args, 't'), NULL);
133 			if (wl == NULL) {
134 				cmdq_error(cmdq,
135 				    "couldn't set '%s'%s", optstr,
136 				    (!args_has(args, 't') && !args_has(args,
137 				    'g')) ? " need target window or -g" : "");
138 				return (CMD_RETURN_ERROR);
139 			}
140 			oo = &wl->window->options;
141 		}
142 	} else if (table == session_options_table) {
143 		if (args_has(self->args, 'g'))
144 			oo = &global_s_options;
145 		else {
146 			s = cmd_find_session(cmdq, args_get(args, 't'), 0);
147 			if (s == NULL) {
148 				cmdq_error(cmdq,
149 				    "couldn't set '%s'%s", optstr,
150 				    (!args_has(args, 't') && !args_has(args,
151 				    'g')) ? " need target session or -g" : "");
152 				return (CMD_RETURN_ERROR);
153 			}
154 			oo = &s->options;
155 		}
156 	} else {
157 		cmdq_error(cmdq, "unknown table");
158 		return (CMD_RETURN_ERROR);
159 	}
160 
161 	/* Unset or set the option. */
162 	if (args_has(args, 'u')) {
163 		if (cmd_set_option_unset(self, cmdq, oe, oo, valstr) != 0)
164 			return (CMD_RETURN_ERROR);
165 	} else {
166 		if (args_has(args, 'o') && options_find1(oo, optstr) != NULL) {
167 			if (!args_has(args, 'q')) {
168 				cmdq_error(cmdq, "already set: %s", optstr);
169 				return (CMD_RETURN_ERROR);
170 			}
171 			return (CMD_RETURN_NORMAL);
172 		}
173 		if (cmd_set_option_set(self, cmdq, oe, oo, valstr) != 0)
174 			return (CMD_RETURN_ERROR);
175 	}
176 
177 	/* Start or stop timers when automatic-rename changed. */
178 	if (strcmp(oe->name, "automatic-rename") == 0) {
179 		for (i = 0; i < ARRAY_LENGTH(&windows); i++) {
180 			if ((w = ARRAY_ITEM(&windows, i)) == NULL)
181 				continue;
182 			if (options_get_number(&w->options, "automatic-rename"))
183 				queue_window_name(w);
184 			else if (event_initialized(&w->name_timer))
185 				evtimer_del(&w->name_timer);
186 		}
187 	}
188 
189 	/* Update sizes and redraw. May not need it but meh. */
190 	recalculate_sizes();
191 	for (i = 0; i < ARRAY_LENGTH(&clients); i++) {
192 		c = ARRAY_ITEM(&clients, i);
193 		if (c != NULL && c->session != NULL)
194 			server_redraw_client(c);
195 	}
196 
197 	return (CMD_RETURN_NORMAL);
198 }
199 
200 /* Set user option. */
201 enum cmd_retval
202 cmd_set_option_user(struct cmd *self, struct cmd_q *cmdq, const char *optstr,
203     const char *valstr)
204 {
205 	struct args	*args = self->args;
206 	struct session	*s;
207 	struct winlink	*wl;
208 	struct options	*oo;
209 
210 	if (args_has(args, 's'))
211 		oo = &global_options;
212 	else if (args_has(self->args, 'w') ||
213 	    self->entry == &cmd_set_window_option_entry) {
214 		if (args_has(self->args, 'g'))
215 			oo = &global_w_options;
216 		else {
217 			wl = cmd_find_window(cmdq, args_get(args, 't'), NULL);
218 			if (wl == NULL)
219 				return (CMD_RETURN_ERROR);
220 			oo = &wl->window->options;
221 		}
222 	} else {
223 		if (args_has(self->args, 'g'))
224 			oo = &global_s_options;
225 		else {
226 			s = cmd_find_session(cmdq, args_get(args, 't'), 0);
227 			if (s == NULL)
228 				return (CMD_RETURN_ERROR);
229 			oo = &s->options;
230 		}
231 	}
232 
233 	if (args_has(args, 'u')) {
234 		if (options_find1(oo, optstr) == NULL) {
235 			if (!args_has(args, 'q')) {
236 				cmdq_error(cmdq, "unknown option: %s", optstr);
237 				return (CMD_RETURN_ERROR);
238 			}
239 			return (CMD_RETURN_NORMAL);
240 		}
241 		if (valstr != NULL) {
242 			cmdq_error(cmdq, "value passed to unset option: %s",
243 			    optstr);
244 			return (CMD_RETURN_ERROR);
245 		}
246 		options_remove(oo, optstr);
247 	} else {
248 		if (valstr == NULL) {
249 			cmdq_error(cmdq, "empty value");
250 			return (CMD_RETURN_ERROR);
251 		}
252 		if (args_has(args, 'o') && options_find1(oo, optstr) != NULL) {
253 			if (!args_has(args, 'q')) {
254 				cmdq_error(cmdq, "already set: %s", optstr);
255 				return (CMD_RETURN_ERROR);
256 			}
257 			return (CMD_RETURN_NORMAL);
258 		}
259 		options_set_string(oo, optstr, "%s", valstr);
260 	}
261 	return (CMD_RETURN_NORMAL);
262 }
263 
264 
265 /* Unset an option. */
266 int
267 cmd_set_option_unset(struct cmd *self, struct cmd_q *cmdq,
268     const struct options_table_entry *oe, struct options *oo,
269     const char *value)
270 {
271 	struct args	*args = self->args;
272 
273 	if (args_has(args, 'g')) {
274 		cmdq_error(cmdq, "can't unset global option: %s", oe->name);
275 		return (-1);
276 	}
277 	if (value != NULL) {
278 		cmdq_error(cmdq, "value passed to unset option: %s", oe->name);
279 		return (-1);
280 	}
281 
282 	options_remove(oo, oe->name);
283 	return (0);
284 }
285 
286 /* Set an option. */
287 int
288 cmd_set_option_set(struct cmd *self, struct cmd_q *cmdq,
289     const struct options_table_entry *oe, struct options *oo,
290     const char *value)
291 {
292 	struct options_entry	*o;
293 
294 	if (oe->type != OPTIONS_TABLE_FLAG && value == NULL) {
295 		cmdq_error(cmdq, "empty value");
296 		return (-1);
297 	}
298 
299 	o = NULL;
300 	switch (oe->type) {
301 	case OPTIONS_TABLE_STRING:
302 		o = cmd_set_option_string(self, cmdq, oe, oo, value);
303 		break;
304 	case OPTIONS_TABLE_NUMBER:
305 		o = cmd_set_option_number(self, cmdq, oe, oo, value);
306 		break;
307 	case OPTIONS_TABLE_KEY:
308 		o = cmd_set_option_key(self, cmdq, oe, oo, value);
309 		break;
310 	case OPTIONS_TABLE_COLOUR:
311 		o = cmd_set_option_colour(self, cmdq, oe, oo, value);
312 		if (o != NULL)
313 			style_update_new(oo, o->name, oe->style);
314 		break;
315 	case OPTIONS_TABLE_ATTRIBUTES:
316 		o = cmd_set_option_attributes(self, cmdq, oe, oo, value);
317 		if (o != NULL)
318 			style_update_new(oo, o->name, oe->style);
319 		break;
320 	case OPTIONS_TABLE_FLAG:
321 		o = cmd_set_option_flag(self, cmdq, oe, oo, value);
322 		break;
323 	case OPTIONS_TABLE_CHOICE:
324 		o = cmd_set_option_choice(self, cmdq, oe, oo, value);
325 		break;
326 	case OPTIONS_TABLE_STYLE:
327 		o = cmd_set_option_style(self, cmdq, oe, oo, value);
328 		break;
329 	}
330 	if (o == NULL)
331 		return (-1);
332 	return (0);
333 }
334 
335 /* Set a string option. */
336 struct options_entry *
337 cmd_set_option_string(struct cmd *self, unused struct cmd_q *cmdq,
338     const struct options_table_entry *oe, struct options *oo,
339     const char *value)
340 {
341 	struct args		*args = self->args;
342 	struct options_entry	*o;
343 	char			*oldval, *newval;
344 
345 	if (args_has(args, 'a')) {
346 		oldval = options_get_string(oo, oe->name);
347 		xasprintf(&newval, "%s%s", oldval, value);
348 	} else
349 		newval = xstrdup(value);
350 
351 	o = options_set_string(oo, oe->name, "%s", newval);
352 
353 	free(newval);
354 	return (o);
355 }
356 
357 /* Set a number option. */
358 struct options_entry *
359 cmd_set_option_number(unused struct cmd *self, struct cmd_q *cmdq,
360     const struct options_table_entry *oe, struct options *oo,
361     const char *value)
362 {
363 	long long	 ll;
364 	const char     	*errstr;
365 
366 	ll = strtonum(value, oe->minimum, oe->maximum, &errstr);
367 	if (errstr != NULL) {
368 		cmdq_error(cmdq, "value is %s: %s", errstr, value);
369 		return (NULL);
370 	}
371 
372 	return (options_set_number(oo, oe->name, ll));
373 }
374 
375 /* Set a key option. */
376 struct options_entry *
377 cmd_set_option_key(unused struct cmd *self, struct cmd_q *cmdq,
378     const struct options_table_entry *oe, struct options *oo,
379     const char *value)
380 {
381 	int	key;
382 
383 	if ((key = key_string_lookup_string(value)) == KEYC_NONE) {
384 		cmdq_error(cmdq, "bad key: %s", value);
385 		return (NULL);
386 	}
387 
388 	return (options_set_number(oo, oe->name, key));
389 }
390 
391 /* Set a colour option. */
392 struct options_entry *
393 cmd_set_option_colour(unused struct cmd *self, struct cmd_q *cmdq,
394     const struct options_table_entry *oe, struct options *oo,
395     const char *value)
396 {
397 	int	colour;
398 
399 	if ((colour = colour_fromstring(value)) == -1) {
400 		cmdq_error(cmdq, "bad colour: %s", value);
401 		return (NULL);
402 	}
403 
404 	return (options_set_number(oo, oe->name, colour));
405 }
406 
407 /* Set an attributes option. */
408 struct options_entry *
409 cmd_set_option_attributes(unused struct cmd *self, struct cmd_q *cmdq,
410     const struct options_table_entry *oe, struct options *oo,
411     const char *value)
412 {
413 	int	attr;
414 
415 	if ((attr = attributes_fromstring(value)) == -1) {
416 		cmdq_error(cmdq, "bad attributes: %s", value);
417 		return (NULL);
418 	}
419 
420 	return (options_set_number(oo, oe->name, attr));
421 }
422 
423 /* Set a flag option. */
424 struct options_entry *
425 cmd_set_option_flag(unused struct cmd *self, struct cmd_q *cmdq,
426     const struct options_table_entry *oe, struct options *oo,
427     const char *value)
428 {
429 	int	flag;
430 
431 	if (value == NULL || *value == '\0')
432 		flag = !options_get_number(oo, oe->name);
433 	else {
434 		if ((value[0] == '1' && value[1] == '\0') ||
435 		    strcasecmp(value, "on") == 0 ||
436 		    strcasecmp(value, "yes") == 0)
437 			flag = 1;
438 		else if ((value[0] == '0' && value[1] == '\0') ||
439 		    strcasecmp(value, "off") == 0 ||
440 		    strcasecmp(value, "no") == 0)
441 			flag = 0;
442 		else {
443 			cmdq_error(cmdq, "bad value: %s", value);
444 			return (NULL);
445 		}
446 	}
447 
448 	return (options_set_number(oo, oe->name, flag));
449 }
450 
451 /* Set a choice option. */
452 struct options_entry *
453 cmd_set_option_choice(unused struct cmd *self, struct cmd_q *cmdq,
454     const struct options_table_entry *oe, struct options *oo,
455     const char *value)
456 {
457 	const char	**choicep;
458 	int		  n, choice = -1;
459 
460 	n = 0;
461 	for (choicep = oe->choices; *choicep != NULL; choicep++) {
462 		n++;
463 		if (strncmp(*choicep, value, strlen(value)) != 0)
464 			continue;
465 
466 		if (choice != -1) {
467 			cmdq_error(cmdq, "ambiguous value: %s", value);
468 			return (NULL);
469 		}
470 		choice = n - 1;
471 	}
472 	if (choice == -1) {
473 		cmdq_error(cmdq, "unknown value: %s", value);
474 		return (NULL);
475 	}
476 
477 	return (options_set_number(oo, oe->name, choice));
478 }
479 
480 /* Set a style option. */
481 struct options_entry *
482 cmd_set_option_style(struct cmd *self, struct cmd_q *cmdq,
483     const struct options_table_entry *oe, struct options *oo,
484     const char *value)
485 {
486 	struct args		*args = self->args;
487 	struct options_entry	*o;
488 	int			 append;
489 
490 	append = args_has(args, 'a');
491 	if ((o = options_set_style(oo, oe->name, value, append)) == NULL) {
492 		cmdq_error(cmdq, "bad style: %s", value);
493 		return (NULL);
494 	}
495 
496 	style_update_old(oo, oe->name, &o->style);
497 	return (o);
498 }
499