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