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