xref: /netbsd-src/external/bsd/tmux/dist/cmd-set-option.c (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
1 /* $OpenBSD$ */
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 /* Unset an option. */
267 int
268 cmd_set_option_unset(struct cmd *self, struct cmd_q *cmdq,
269     const struct options_table_entry *oe, struct options *oo,
270     const char *value)
271 {
272 	struct args	*args = self->args;
273 
274 	if (value != NULL) {
275 		cmdq_error(cmdq, "value passed to unset option: %s", oe->name);
276 		return (-1);
277 	}
278 
279 	if (args_has(args, 'g') || oo == &global_options) {
280 		switch (oe->type) {
281 		case OPTIONS_TABLE_STRING:
282 			options_set_string(oo, oe->name, "%s", oe->default_str);
283 			break;
284 		case OPTIONS_TABLE_STYLE:
285 			options_set_style(oo, oe->name, oe->default_str, 0);
286 			break;
287 		default:
288 			options_set_number(oo, oe->name, oe->default_num);
289 			break;
290 		}
291 	} else
292 		options_remove(oo, oe->name);
293 	return (0);
294 }
295 
296 /* Set an option. */
297 int
298 cmd_set_option_set(struct cmd *self, struct cmd_q *cmdq,
299     const struct options_table_entry *oe, struct options *oo,
300     const char *value)
301 {
302 	struct options_entry	*o;
303 
304 	switch (oe->type) {
305 	case OPTIONS_TABLE_FLAG:
306 	case OPTIONS_TABLE_CHOICE:
307 		break;
308 	default:
309 		if (value == NULL) {
310 			cmdq_error(cmdq, "empty value");
311 			return (-1);
312 		}
313 	}
314 
315 	o = NULL;
316 	switch (oe->type) {
317 	case OPTIONS_TABLE_STRING:
318 		o = cmd_set_option_string(self, cmdq, oe, oo, value);
319 		break;
320 	case OPTIONS_TABLE_NUMBER:
321 		o = cmd_set_option_number(self, cmdq, oe, oo, value);
322 		break;
323 	case OPTIONS_TABLE_KEY:
324 		o = cmd_set_option_key(self, cmdq, oe, oo, value);
325 		break;
326 	case OPTIONS_TABLE_COLOUR:
327 		o = cmd_set_option_colour(self, cmdq, oe, oo, value);
328 		if (o != NULL)
329 			style_update_new(oo, o->name, oe->style);
330 		break;
331 	case OPTIONS_TABLE_ATTRIBUTES:
332 		o = cmd_set_option_attributes(self, cmdq, oe, oo, value);
333 		if (o != NULL)
334 			style_update_new(oo, o->name, oe->style);
335 		break;
336 	case OPTIONS_TABLE_FLAG:
337 		o = cmd_set_option_flag(self, cmdq, oe, oo, value);
338 		break;
339 	case OPTIONS_TABLE_CHOICE:
340 		o = cmd_set_option_choice(self, cmdq, oe, oo, value);
341 		break;
342 	case OPTIONS_TABLE_STYLE:
343 		o = cmd_set_option_style(self, cmdq, oe, oo, value);
344 		break;
345 	}
346 	if (o == NULL)
347 		return (-1);
348 	return (0);
349 }
350 
351 /* Set a string option. */
352 struct options_entry *
353 cmd_set_option_string(struct cmd *self, unused struct cmd_q *cmdq,
354     const struct options_table_entry *oe, struct options *oo,
355     const char *value)
356 {
357 	struct args		*args = self->args;
358 	struct options_entry	*o;
359 	char			*oldval, *newval;
360 
361 	if (args_has(args, 'a')) {
362 		oldval = options_get_string(oo, oe->name);
363 		xasprintf(&newval, "%s%s", oldval, value);
364 	} else
365 		newval = xstrdup(value);
366 
367 	o = options_set_string(oo, oe->name, "%s", newval);
368 
369 	free(newval);
370 	return (o);
371 }
372 
373 /* Set a number option. */
374 struct options_entry *
375 cmd_set_option_number(unused struct cmd *self, struct cmd_q *cmdq,
376     const struct options_table_entry *oe, struct options *oo,
377     const char *value)
378 {
379 	long long	 ll;
380 	const char     	*errstr;
381 
382 	ll = strtonum(value, oe->minimum, oe->maximum, &errstr);
383 	if (errstr != NULL) {
384 		cmdq_error(cmdq, "value is %s: %s", errstr, value);
385 		return (NULL);
386 	}
387 
388 	return (options_set_number(oo, oe->name, ll));
389 }
390 
391 /* Set a key option. */
392 struct options_entry *
393 cmd_set_option_key(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	key;
398 
399 	if ((key = key_string_lookup_string(value)) == KEYC_NONE) {
400 		cmdq_error(cmdq, "bad key: %s", value);
401 		return (NULL);
402 	}
403 
404 	return (options_set_number(oo, oe->name, key));
405 }
406 
407 /* Set a colour option. */
408 struct options_entry *
409 cmd_set_option_colour(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	colour;
414 
415 	if ((colour = colour_fromstring(value)) == -1) {
416 		cmdq_error(cmdq, "bad colour: %s", value);
417 		return (NULL);
418 	}
419 
420 	return (options_set_number(oo, oe->name, colour));
421 }
422 
423 /* Set an attributes option. */
424 struct options_entry *
425 cmd_set_option_attributes(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	attr;
430 
431 	if ((attr = attributes_fromstring(value)) == -1) {
432 		cmdq_error(cmdq, "bad attributes: %s", value);
433 		return (NULL);
434 	}
435 
436 	return (options_set_number(oo, oe->name, attr));
437 }
438 
439 /* Set a flag option. */
440 struct options_entry *
441 cmd_set_option_flag(unused struct cmd *self, struct cmd_q *cmdq,
442     const struct options_table_entry *oe, struct options *oo,
443     const char *value)
444 {
445 	int	flag;
446 
447 	if (value == NULL || *value == '\0')
448 		flag = !options_get_number(oo, oe->name);
449 	else {
450 		if ((value[0] == '1' && value[1] == '\0') ||
451 		    strcasecmp(value, "on") == 0 ||
452 		    strcasecmp(value, "yes") == 0)
453 			flag = 1;
454 		else if ((value[0] == '0' && value[1] == '\0') ||
455 		    strcasecmp(value, "off") == 0 ||
456 		    strcasecmp(value, "no") == 0)
457 			flag = 0;
458 		else {
459 			cmdq_error(cmdq, "bad value: %s", value);
460 			return (NULL);
461 		}
462 	}
463 
464 	return (options_set_number(oo, oe->name, flag));
465 }
466 
467 /* Set a choice option. */
468 struct options_entry *
469 cmd_set_option_choice(unused struct cmd *self, struct cmd_q *cmdq,
470     const struct options_table_entry *oe, struct options *oo,
471     const char *value)
472 {
473 	const char	**choicep;
474 	int		  n, choice = -1;
475 
476 	if (value == NULL) {
477 		choice = options_get_number(oo, oe->name);
478 		if (choice < 2)
479 			choice = !choice;
480 	} else {
481 		n = 0;
482 		for (choicep = oe->choices; *choicep != NULL; choicep++) {
483 			n++;
484 			if (strncmp(*choicep, value, strlen(value)) != 0)
485 				continue;
486 
487 			if (choice != -1) {
488 				cmdq_error(cmdq, "ambiguous value: %s", value);
489 				return (NULL);
490 			}
491 			choice = n - 1;
492 		}
493 		if (choice == -1) {
494 			cmdq_error(cmdq, "unknown value: %s", value);
495 			return (NULL);
496 		}
497 	}
498 
499 	return (options_set_number(oo, oe->name, choice));
500 }
501 
502 /* Set a style option. */
503 struct options_entry *
504 cmd_set_option_style(struct cmd *self, struct cmd_q *cmdq,
505     const struct options_table_entry *oe, struct options *oo,
506     const char *value)
507 {
508 	struct args		*args = self->args;
509 	struct options_entry	*o;
510 	int			 append;
511 
512 	append = args_has(args, 'a');
513 	if ((o = options_set_style(oo, oe->name, value, append)) == NULL) {
514 		cmdq_error(cmdq, "bad style: %s", value);
515 		return (NULL);
516 	}
517 
518 	style_update_old(oo, oe->name, &o->style);
519 	return (o);
520 }
521