xref: /openbsd/usr.bin/tmux/options.c (revision d5f1c1a5)
1 /* $OpenBSD: options.c,v 1.72 2024/11/11 08:41:05 nicm Exp $ */
2 
3 /*
4  * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
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 <ctype.h>
22 #include <fnmatch.h>
23 #include <stdarg.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #include "tmux.h"
28 
29 /*
30  * Option handling; each option has a name, type and value and is stored in
31  * a red-black tree.
32  */
33 
34 struct options_array_item {
35 	u_int				 index;
36 	union options_value		 value;
37 	RB_ENTRY(options_array_item)	 entry;
38 };
39 static int
options_array_cmp(struct options_array_item * a1,struct options_array_item * a2)40 options_array_cmp(struct options_array_item *a1, struct options_array_item *a2)
41 {
42 	if (a1->index < a2->index)
43 		return (-1);
44 	if (a1->index > a2->index)
45 		return (1);
46 	return (0);
47 }
48 RB_GENERATE_STATIC(options_array, options_array_item, entry, options_array_cmp);
49 
50 struct options_entry {
51 	struct options				*owner;
52 
53 	const char				*name;
54 	const struct options_table_entry	*tableentry;
55 	union options_value			 value;
56 
57 	int					 cached;
58 	struct style				 style;
59 
60 	RB_ENTRY(options_entry)			 entry;
61 };
62 
63 struct options {
64 	RB_HEAD(options_tree, options_entry)	 tree;
65 	struct options				*parent;
66 };
67 
68 static struct options_entry	*options_add(struct options *, const char *);
69 static void			 options_remove(struct options_entry *);
70 
71 #define OPTIONS_IS_STRING(o)						\
72 	((o)->tableentry == NULL ||					\
73 	    (o)->tableentry->type == OPTIONS_TABLE_STRING)
74 #define OPTIONS_IS_NUMBER(o) \
75 	((o)->tableentry != NULL &&					\
76 	    ((o)->tableentry->type == OPTIONS_TABLE_NUMBER ||		\
77 	    (o)->tableentry->type == OPTIONS_TABLE_KEY ||		\
78 	    (o)->tableentry->type == OPTIONS_TABLE_COLOUR ||		\
79 	    (o)->tableentry->type == OPTIONS_TABLE_FLAG ||		\
80 	    (o)->tableentry->type == OPTIONS_TABLE_CHOICE))
81 #define OPTIONS_IS_COMMAND(o) \
82 	((o)->tableentry != NULL &&					\
83 	    (o)->tableentry->type == OPTIONS_TABLE_COMMAND)
84 
85 #define OPTIONS_IS_ARRAY(o)						\
86 	((o)->tableentry != NULL &&					\
87 	    ((o)->tableentry->flags & OPTIONS_TABLE_IS_ARRAY))
88 
89 static int	options_cmp(struct options_entry *, struct options_entry *);
90 RB_GENERATE_STATIC(options_tree, options_entry, entry, options_cmp);
91 
92 static int
options_cmp(struct options_entry * lhs,struct options_entry * rhs)93 options_cmp(struct options_entry *lhs, struct options_entry *rhs)
94 {
95 	return (strcmp(lhs->name, rhs->name));
96 }
97 
98 static const char *
options_map_name(const char * name)99 options_map_name(const char *name)
100 {
101 	const struct options_name_map	*map;
102 
103 	for (map = options_other_names; map->from != NULL; map++) {
104 		if (strcmp(map->from, name) == 0)
105 			return (map->to);
106 	}
107 	return (name);
108 }
109 
110 static const struct options_table_entry *
options_parent_table_entry(struct options * oo,const char * s)111 options_parent_table_entry(struct options *oo, const char *s)
112 {
113 	struct options_entry	*o;
114 
115 	if (oo->parent == NULL)
116 		fatalx("no parent options for %s", s);
117 	o = options_get(oo->parent, s);
118 	if (o == NULL)
119 		fatalx("%s not in parent options", s);
120 	return (o->tableentry);
121 }
122 
123 static void
options_value_free(struct options_entry * o,union options_value * ov)124 options_value_free(struct options_entry *o, union options_value *ov)
125 {
126 	if (OPTIONS_IS_STRING(o))
127 		free(ov->string);
128 	if (OPTIONS_IS_COMMAND(o) && ov->cmdlist != NULL)
129 		cmd_list_free(ov->cmdlist);
130 }
131 
132 static char *
options_value_to_string(struct options_entry * o,union options_value * ov,int numeric)133 options_value_to_string(struct options_entry *o, union options_value *ov,
134     int numeric)
135 {
136 	char	*s;
137 
138 	if (OPTIONS_IS_COMMAND(o))
139 		return (cmd_list_print(ov->cmdlist, 0));
140 	if (OPTIONS_IS_NUMBER(o)) {
141 		switch (o->tableentry->type) {
142 		case OPTIONS_TABLE_NUMBER:
143 			xasprintf(&s, "%lld", ov->number);
144 			break;
145 		case OPTIONS_TABLE_KEY:
146 			s = xstrdup(key_string_lookup_key(ov->number, 0));
147 			break;
148 		case OPTIONS_TABLE_COLOUR:
149 			s = xstrdup(colour_tostring(ov->number));
150 			break;
151 		case OPTIONS_TABLE_FLAG:
152 			if (numeric)
153 				xasprintf(&s, "%lld", ov->number);
154 			else
155 				s = xstrdup(ov->number ? "on" : "off");
156 			break;
157 		case OPTIONS_TABLE_CHOICE:
158 			s = xstrdup(o->tableentry->choices[ov->number]);
159 			break;
160 		default:
161 			fatalx("not a number option type");
162 		}
163 		return (s);
164 	}
165 	if (OPTIONS_IS_STRING(o))
166 		return (xstrdup(ov->string));
167 	return (xstrdup(""));
168 }
169 
170 struct options *
options_create(struct options * parent)171 options_create(struct options *parent)
172 {
173 	struct options	*oo;
174 
175 	oo = xcalloc(1, sizeof *oo);
176 	RB_INIT(&oo->tree);
177 	oo->parent = parent;
178 	return (oo);
179 }
180 
181 void
options_free(struct options * oo)182 options_free(struct options *oo)
183 {
184 	struct options_entry	*o, *tmp;
185 
186 	RB_FOREACH_SAFE(o, options_tree, &oo->tree, tmp)
187 		options_remove(o);
188 	free(oo);
189 }
190 
191 struct options *
options_get_parent(struct options * oo)192 options_get_parent(struct options *oo)
193 {
194 	return (oo->parent);
195 }
196 
197 void
options_set_parent(struct options * oo,struct options * parent)198 options_set_parent(struct options *oo, struct options *parent)
199 {
200 	oo->parent = parent;
201 }
202 
203 struct options_entry *
options_first(struct options * oo)204 options_first(struct options *oo)
205 {
206 	return (RB_MIN(options_tree, &oo->tree));
207 }
208 
209 struct options_entry *
options_next(struct options_entry * o)210 options_next(struct options_entry *o)
211 {
212 	return (RB_NEXT(options_tree, &oo->tree, o));
213 }
214 
215 struct options_entry *
options_get_only(struct options * oo,const char * name)216 options_get_only(struct options *oo, const char *name)
217 {
218 	struct options_entry	o = { .name = name }, *found;
219 
220 	found = RB_FIND(options_tree, &oo->tree, &o);
221 	if (found == NULL) {
222 		o.name = options_map_name(name);
223 		return (RB_FIND(options_tree, &oo->tree, &o));
224 	}
225 	return (found);
226 }
227 
228 struct options_entry *
options_get(struct options * oo,const char * name)229 options_get(struct options *oo, const char *name)
230 {
231 	struct options_entry	*o;
232 
233 	o = options_get_only(oo, name);
234 	while (o == NULL) {
235 		oo = oo->parent;
236 		if (oo == NULL)
237 			break;
238 		o = options_get_only(oo, name);
239 	}
240 	return (o);
241 }
242 
243 struct options_entry *
options_empty(struct options * oo,const struct options_table_entry * oe)244 options_empty(struct options *oo, const struct options_table_entry *oe)
245 {
246 	struct options_entry	*o;
247 
248 	o = options_add(oo, oe->name);
249 	o->tableentry = oe;
250 
251 	if (oe->flags & OPTIONS_TABLE_IS_ARRAY)
252 		RB_INIT(&o->value.array);
253 
254 	return (o);
255 }
256 
257 struct options_entry *
options_default(struct options * oo,const struct options_table_entry * oe)258 options_default(struct options *oo, const struct options_table_entry *oe)
259 {
260 	struct options_entry	*o;
261 	union options_value	*ov;
262 	u_int			 i;
263 
264 	o = options_empty(oo, oe);
265 	ov = &o->value;
266 
267 	if (oe->flags & OPTIONS_TABLE_IS_ARRAY) {
268 		if (oe->default_arr == NULL) {
269 			options_array_assign(o, oe->default_str, NULL);
270 			return (o);
271 		}
272 		for (i = 0; oe->default_arr[i] != NULL; i++)
273 			options_array_set(o, i, oe->default_arr[i], 0, NULL);
274 		return (o);
275 	}
276 
277 	switch (oe->type) {
278 	case OPTIONS_TABLE_STRING:
279 		ov->string = xstrdup(oe->default_str);
280 		break;
281 	default:
282 		ov->number = oe->default_num;
283 		break;
284 	}
285 	return (o);
286 }
287 
288 char *
options_default_to_string(const struct options_table_entry * oe)289 options_default_to_string(const struct options_table_entry *oe)
290 {
291 	char	*s;
292 
293 	switch (oe->type) {
294 	case OPTIONS_TABLE_STRING:
295 	case OPTIONS_TABLE_COMMAND:
296 		s = xstrdup(oe->default_str);
297 		break;
298 	case OPTIONS_TABLE_NUMBER:
299 		xasprintf(&s, "%lld", oe->default_num);
300 		break;
301 	case OPTIONS_TABLE_KEY:
302 		s = xstrdup(key_string_lookup_key(oe->default_num, 0));
303 		break;
304 	case OPTIONS_TABLE_COLOUR:
305 		s = xstrdup(colour_tostring(oe->default_num));
306 		break;
307 	case OPTIONS_TABLE_FLAG:
308 		s = xstrdup(oe->default_num ? "on" : "off");
309 		break;
310 	case OPTIONS_TABLE_CHOICE:
311 		s = xstrdup(oe->choices[oe->default_num]);
312 		break;
313 	default:
314 		fatalx("unknown option type");
315 	}
316 	return (s);
317 }
318 
319 static struct options_entry *
options_add(struct options * oo,const char * name)320 options_add(struct options *oo, const char *name)
321 {
322 	struct options_entry	*o;
323 
324 	o = options_get_only(oo, name);
325 	if (o != NULL)
326 		options_remove(o);
327 
328 	o = xcalloc(1, sizeof *o);
329 	o->owner = oo;
330 	o->name = xstrdup(name);
331 
332 	RB_INSERT(options_tree, &oo->tree, o);
333 	return (o);
334 }
335 
336 static void
options_remove(struct options_entry * o)337 options_remove(struct options_entry *o)
338 {
339 	struct options	*oo = o->owner;
340 
341 	if (OPTIONS_IS_ARRAY(o))
342 		options_array_clear(o);
343 	else
344 		options_value_free(o, &o->value);
345 	RB_REMOVE(options_tree, &oo->tree, o);
346 	free((void *)o->name);
347 	free(o);
348 }
349 
350 const char *
options_name(struct options_entry * o)351 options_name(struct options_entry *o)
352 {
353 	return (o->name);
354 }
355 
356 struct options *
options_owner(struct options_entry * o)357 options_owner(struct options_entry *o)
358 {
359 	return (o->owner);
360 }
361 
362 const struct options_table_entry *
options_table_entry(struct options_entry * o)363 options_table_entry(struct options_entry *o)
364 {
365 	return (o->tableentry);
366 }
367 
368 static struct options_array_item *
options_array_item(struct options_entry * o,u_int idx)369 options_array_item(struct options_entry *o, u_int idx)
370 {
371 	struct options_array_item	a;
372 
373 	a.index = idx;
374 	return (RB_FIND(options_array, &o->value.array, &a));
375 }
376 
377 static struct options_array_item *
options_array_new(struct options_entry * o,u_int idx)378 options_array_new(struct options_entry *o, u_int idx)
379 {
380 	struct options_array_item	*a;
381 
382 	a = xcalloc(1, sizeof *a);
383 	a->index = idx;
384 	RB_INSERT(options_array, &o->value.array, a);
385 	return (a);
386 }
387 
388 static void
options_array_free(struct options_entry * o,struct options_array_item * a)389 options_array_free(struct options_entry *o, struct options_array_item *a)
390 {
391 	options_value_free(o, &a->value);
392 	RB_REMOVE(options_array, &o->value.array, a);
393 	free(a);
394 }
395 
396 void
options_array_clear(struct options_entry * o)397 options_array_clear(struct options_entry *o)
398 {
399 	struct options_array_item	*a, *a1;
400 
401 	if (!OPTIONS_IS_ARRAY(o))
402 		return;
403 
404 	RB_FOREACH_SAFE(a, options_array, &o->value.array, a1)
405 		options_array_free(o, a);
406 }
407 
408 union options_value *
options_array_get(struct options_entry * o,u_int idx)409 options_array_get(struct options_entry *o, u_int idx)
410 {
411 	struct options_array_item	*a;
412 
413 	if (!OPTIONS_IS_ARRAY(o))
414 		return (NULL);
415 	a = options_array_item(o, idx);
416 	if (a == NULL)
417 		return (NULL);
418 	return (&a->value);
419 }
420 
421 int
options_array_set(struct options_entry * o,u_int idx,const char * value,int append,char ** cause)422 options_array_set(struct options_entry *o, u_int idx, const char *value,
423     int append, char **cause)
424 {
425 	struct options_array_item	*a;
426 	char				*new;
427 	struct cmd_parse_result		*pr;
428 	long long		 	 number;
429 
430 	if (!OPTIONS_IS_ARRAY(o)) {
431 		if (cause != NULL)
432 			*cause = xstrdup("not an array");
433 		return (-1);
434 	}
435 
436 	if (value == NULL) {
437 		a = options_array_item(o, idx);
438 		if (a != NULL)
439 			options_array_free(o, a);
440 		return (0);
441 	}
442 
443 	if (OPTIONS_IS_COMMAND(o)) {
444 		pr = cmd_parse_from_string(value, NULL);
445 		switch (pr->status) {
446 		case CMD_PARSE_ERROR:
447 			if (cause != NULL)
448 				*cause = pr->error;
449 			else
450 				free(pr->error);
451 			return (-1);
452 		case CMD_PARSE_SUCCESS:
453 			break;
454 		}
455 
456 		a = options_array_item(o, idx);
457 		if (a == NULL)
458 			a = options_array_new(o, idx);
459 		else
460 			options_value_free(o, &a->value);
461 		a->value.cmdlist = pr->cmdlist;
462 		return (0);
463 	}
464 
465 	if (OPTIONS_IS_STRING(o)) {
466 		a = options_array_item(o, idx);
467 		if (a != NULL && append)
468 			xasprintf(&new, "%s%s", a->value.string, value);
469 		else
470 			new = xstrdup(value);
471 		if (a == NULL)
472 			a = options_array_new(o, idx);
473 		else
474 			options_value_free(o, &a->value);
475 		a->value.string = new;
476 		return (0);
477 	}
478 
479 	if (o->tableentry->type == OPTIONS_TABLE_COLOUR) {
480 		if ((number = colour_fromstring(value)) == -1) {
481 			xasprintf(cause, "bad colour: %s", value);
482 			return (-1);
483 		}
484 		a = options_array_item(o, idx);
485 		if (a == NULL)
486 			a = options_array_new(o, idx);
487 		else
488 			options_value_free(o, &a->value);
489 		a->value.number = number;
490 		return (0);
491 	}
492 
493 	if (cause != NULL)
494 		*cause = xstrdup("wrong array type");
495 	return (-1);
496 }
497 
498 int
options_array_assign(struct options_entry * o,const char * s,char ** cause)499 options_array_assign(struct options_entry *o, const char *s, char **cause)
500 {
501 	const char	*separator;
502 	char		*copy, *next, *string;
503 	u_int		 i;
504 
505 	separator = o->tableentry->separator;
506 	if (separator == NULL)
507 		separator = " ,";
508 	if (*separator == '\0') {
509 		if (*s == '\0')
510 			return (0);
511 		for (i = 0; i < UINT_MAX; i++) {
512 			if (options_array_item(o, i) == NULL)
513 				break;
514 		}
515 		return (options_array_set(o, i, s, 0, cause));
516 	}
517 
518 	if (*s == '\0')
519 		return (0);
520 	copy = string = xstrdup(s);
521 	while ((next = strsep(&string, separator)) != NULL) {
522 		if (*next == '\0')
523 			continue;
524 		for (i = 0; i < UINT_MAX; i++) {
525 			if (options_array_item(o, i) == NULL)
526 				break;
527 		}
528 		if (i == UINT_MAX)
529 			break;
530 		if (options_array_set(o, i, next, 0, cause) != 0) {
531 			free(copy);
532 			return (-1);
533 		}
534 	}
535 	free(copy);
536 	return (0);
537 }
538 
539 struct options_array_item *
options_array_first(struct options_entry * o)540 options_array_first(struct options_entry *o)
541 {
542 	if (!OPTIONS_IS_ARRAY(o))
543 		return (NULL);
544 	return (RB_MIN(options_array, &o->value.array));
545 }
546 
547 struct options_array_item *
options_array_next(struct options_array_item * a)548 options_array_next(struct options_array_item *a)
549 {
550 	return (RB_NEXT(options_array, &o->value.array, a));
551 }
552 
553 u_int
options_array_item_index(struct options_array_item * a)554 options_array_item_index(struct options_array_item *a)
555 {
556 	return (a->index);
557 }
558 
559 union options_value *
options_array_item_value(struct options_array_item * a)560 options_array_item_value(struct options_array_item *a)
561 {
562 	return (&a->value);
563 }
564 
565 int
options_is_array(struct options_entry * o)566 options_is_array(struct options_entry *o)
567 {
568 	return (OPTIONS_IS_ARRAY(o));
569 }
570 
571 int
options_is_string(struct options_entry * o)572 options_is_string(struct options_entry *o)
573 {
574 	return (OPTIONS_IS_STRING(o));
575 }
576 
577 char *
options_to_string(struct options_entry * o,int idx,int numeric)578 options_to_string(struct options_entry *o, int idx, int numeric)
579 {
580 	struct options_array_item	*a;
581 	char				*result = NULL;
582 	char				*last = NULL;
583 	char				*next;
584 
585 	if (OPTIONS_IS_ARRAY(o)) {
586 		if (idx == -1) {
587 			RB_FOREACH(a, options_array, &o->value.array) {
588 				next = options_value_to_string(o, &a->value,
589 				    numeric);
590 				if (last == NULL)
591 					result = next;
592 				else {
593 					xasprintf(&result, "%s %s", last, next);
594 					free(last);
595 					free(next);
596 				}
597 				last = result;
598 			}
599 			if (result == NULL)
600 				return (xstrdup(""));
601 			return (result);
602 		}
603 		a = options_array_item(o, idx);
604 		if (a == NULL)
605 			return (xstrdup(""));
606 		return (options_value_to_string(o, &a->value, numeric));
607 	}
608 	return (options_value_to_string(o, &o->value, numeric));
609 }
610 
611 char *
options_parse(const char * name,int * idx)612 options_parse(const char *name, int *idx)
613 {
614 	char	*copy, *cp, *end;
615 
616 	if (*name == '\0')
617 		return (NULL);
618 	copy = xstrdup(name);
619 	if ((cp = strchr(copy, '[')) == NULL) {
620 		*idx = -1;
621 		return (copy);
622 	}
623 	end = strchr(cp + 1, ']');
624 	if (end == NULL || end[1] != '\0' || !isdigit((u_char)end[-1])) {
625 		free(copy);
626 		return (NULL);
627 	}
628 	if (sscanf(cp, "[%d]", idx) != 1 || *idx < 0) {
629 		free(copy);
630 		return (NULL);
631 	}
632 	*cp = '\0';
633 	return (copy);
634 }
635 
636 struct options_entry *
options_parse_get(struct options * oo,const char * s,int * idx,int only)637 options_parse_get(struct options *oo, const char *s, int *idx, int only)
638 {
639 	struct options_entry	*o;
640 	char			*name;
641 
642 	name = options_parse(s, idx);
643 	if (name == NULL)
644 		return (NULL);
645 	if (only)
646 		o = options_get_only(oo, name);
647 	else
648 		o = options_get(oo, name);
649 	free(name);
650 	return (o);
651 }
652 
653 char *
options_match(const char * s,int * idx,int * ambiguous)654 options_match(const char *s, int *idx, int *ambiguous)
655 {
656 	const struct options_table_entry	*oe, *found;
657 	char					*parsed;
658 	const char				*name;
659 	size_t					 namelen;
660 
661 	parsed = options_parse(s, idx);
662 	if (parsed == NULL)
663 		return (NULL);
664 	if (*parsed == '@') {
665 		*ambiguous = 0;
666 		return (parsed);
667 	}
668 
669 	name = options_map_name(parsed);
670 	namelen = strlen(name);
671 
672 	found = NULL;
673 	for (oe = options_table; oe->name != NULL; oe++) {
674 		if (strcmp(oe->name, name) == 0) {
675 			found = oe;
676 			break;
677 		}
678 		if (strncmp(oe->name, name, namelen) == 0) {
679 			if (found != NULL) {
680 				*ambiguous = 1;
681 				free(parsed);
682 				return (NULL);
683 			}
684 			found = oe;
685 		}
686 	}
687 	free(parsed);
688 	if (found == NULL) {
689 		*ambiguous = 0;
690 		return (NULL);
691 	}
692 	return (xstrdup(found->name));
693 }
694 
695 struct options_entry *
options_match_get(struct options * oo,const char * s,int * idx,int only,int * ambiguous)696 options_match_get(struct options *oo, const char *s, int *idx, int only,
697     int *ambiguous)
698 {
699 	char			*name;
700 	struct options_entry	*o;
701 
702 	name = options_match(s, idx, ambiguous);
703 	if (name == NULL)
704 		return (NULL);
705 	*ambiguous = 0;
706 	if (only)
707 		o = options_get_only(oo, name);
708 	else
709 		o = options_get(oo, name);
710 	free(name);
711 	return (o);
712 }
713 
714 const char *
options_get_string(struct options * oo,const char * name)715 options_get_string(struct options *oo, const char *name)
716 {
717 	struct options_entry	*o;
718 
719 	o = options_get(oo, name);
720 	if (o == NULL)
721 		fatalx("missing option %s", name);
722 	if (!OPTIONS_IS_STRING(o))
723 		fatalx("option %s is not a string", name);
724 	return (o->value.string);
725 }
726 
727 long long
options_get_number(struct options * oo,const char * name)728 options_get_number(struct options *oo, const char *name)
729 {
730 	struct options_entry	*o;
731 
732 	o = options_get(oo, name);
733 	if (o == NULL)
734 		fatalx("missing option %s", name);
735 	if (!OPTIONS_IS_NUMBER(o))
736 		fatalx("option %s is not a number", name);
737 	return (o->value.number);
738 }
739 
740 struct options_entry *
options_set_string(struct options * oo,const char * name,int append,const char * fmt,...)741 options_set_string(struct options *oo, const char *name, int append,
742     const char *fmt, ...)
743 {
744 	struct options_entry	*o;
745 	va_list			 ap;
746 	const char		*separator = "";
747 	char			*s, *value;
748 
749 	va_start(ap, fmt);
750 	xvasprintf(&s, fmt, ap);
751 	va_end(ap);
752 
753 	o = options_get_only(oo, name);
754 	if (o != NULL && append && OPTIONS_IS_STRING(o)) {
755 		if (*name != '@') {
756 			separator = o->tableentry->separator;
757 			if (separator == NULL)
758 				separator = "";
759 		}
760 		xasprintf(&value, "%s%s%s", o->value.string, separator, s);
761 		free(s);
762 	} else
763 		value = s;
764 	if (o == NULL && *name == '@')
765 		o = options_add(oo, name);
766 	else if (o == NULL) {
767 		o = options_default(oo, options_parent_table_entry(oo, name));
768 		if (o == NULL)
769 			return (NULL);
770 	}
771 
772 	if (!OPTIONS_IS_STRING(o))
773 		fatalx("option %s is not a string", name);
774 	free(o->value.string);
775 	o->value.string = value;
776 	o->cached = 0;
777 	return (o);
778 }
779 
780 struct options_entry *
options_set_number(struct options * oo,const char * name,long long value)781 options_set_number(struct options *oo, const char *name, long long value)
782 {
783 	struct options_entry	*o;
784 
785 	if (*name == '@')
786 		fatalx("user option %s must be a string", name);
787 
788 	o = options_get_only(oo, name);
789 	if (o == NULL) {
790 		o = options_default(oo, options_parent_table_entry(oo, name));
791 		if (o == NULL)
792 			return (NULL);
793 	}
794 
795 	if (!OPTIONS_IS_NUMBER(o))
796 		fatalx("option %s is not a number", name);
797 	o->value.number = value;
798 	return (o);
799 }
800 
801 int
options_scope_from_name(struct args * args,int window,const char * name,struct cmd_find_state * fs,struct options ** oo,char ** cause)802 options_scope_from_name(struct args *args, int window,
803     const char *name, struct cmd_find_state *fs, struct options **oo,
804     char **cause)
805 {
806 	struct session				*s = fs->s;
807 	struct winlink				*wl = fs->wl;
808 	struct window_pane			*wp = fs->wp;
809 	const char				*target = args_get(args, 't');
810 	const struct options_table_entry	*oe;
811 	int					 scope = OPTIONS_TABLE_NONE;
812 
813 	if (*name == '@')
814 		return (options_scope_from_flags(args, window, fs, oo, cause));
815 
816 	for (oe = options_table; oe->name != NULL; oe++) {
817 		if (strcmp(oe->name, name) == 0)
818 			break;
819 	}
820 	if (oe->name == NULL) {
821 		xasprintf(cause, "unknown option: %s", name);
822 		return (OPTIONS_TABLE_NONE);
823 	}
824 	switch (oe->scope) {
825 	case OPTIONS_TABLE_SERVER:
826 		*oo = global_options;
827 		scope = OPTIONS_TABLE_SERVER;
828 		break;
829 	case OPTIONS_TABLE_SESSION:
830 		if (args_has(args, 'g')) {
831 			*oo = global_s_options;
832 			scope = OPTIONS_TABLE_SESSION;
833 		} else if (s == NULL && target != NULL)
834 			xasprintf(cause, "no such session: %s", target);
835 		else if (s == NULL)
836 			xasprintf(cause, "no current session");
837 		else {
838 			*oo = s->options;
839 			scope = OPTIONS_TABLE_SESSION;
840 		}
841 		break;
842 	case OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE:
843 		if (args_has(args, 'p')) {
844 			if (wp == NULL && target != NULL)
845 				xasprintf(cause, "no such pane: %s", target);
846 			else if (wp == NULL)
847 				xasprintf(cause, "no current pane");
848 			else {
849 				*oo = wp->options;
850 				scope = OPTIONS_TABLE_PANE;
851 			}
852 			break;
853 		}
854 		/* FALLTHROUGH */
855 	case OPTIONS_TABLE_WINDOW:
856 		if (args_has(args, 'g')) {
857 			*oo = global_w_options;
858 			scope = OPTIONS_TABLE_WINDOW;
859 		} else if (wl == NULL && target != NULL)
860 			xasprintf(cause, "no such window: %s", target);
861 		else if (wl == NULL)
862 			xasprintf(cause, "no current window");
863 		else {
864 			*oo = wl->window->options;
865 			scope = OPTIONS_TABLE_WINDOW;
866 		}
867 		break;
868 	}
869 	return (scope);
870 }
871 
872 int
options_scope_from_flags(struct args * args,int window,struct cmd_find_state * fs,struct options ** oo,char ** cause)873 options_scope_from_flags(struct args *args, int window,
874     struct cmd_find_state *fs, struct options **oo, char **cause)
875 {
876 	struct session		*s = fs->s;
877 	struct winlink		*wl = fs->wl;
878 	struct window_pane	*wp = fs->wp;
879 	const char		*target = args_get(args, 't');
880 
881 	if (args_has(args, 's')) {
882 		*oo = global_options;
883 		return (OPTIONS_TABLE_SERVER);
884 	}
885 
886 	if (args_has(args, 'p')) {
887 		if (wp == NULL) {
888 			if (target != NULL)
889 				xasprintf(cause, "no such pane: %s", target);
890 			else
891 				xasprintf(cause, "no current pane");
892 			return (OPTIONS_TABLE_NONE);
893 		}
894 		*oo = wp->options;
895 		return (OPTIONS_TABLE_PANE);
896 	} else if (window || args_has(args, 'w')) {
897 		if (args_has(args, 'g')) {
898 			*oo = global_w_options;
899 			return (OPTIONS_TABLE_WINDOW);
900 		}
901 		if (wl == NULL) {
902 			if (target != NULL)
903 				xasprintf(cause, "no such window: %s", target);
904 			else
905 				xasprintf(cause, "no current window");
906 			return (OPTIONS_TABLE_NONE);
907 		}
908 		*oo = wl->window->options;
909 		return (OPTIONS_TABLE_WINDOW);
910 	} else {
911 		if (args_has(args, 'g')) {
912 			*oo = global_s_options;
913 			return (OPTIONS_TABLE_SESSION);
914 		}
915 		if (s == NULL) {
916 			if (target != NULL)
917 				xasprintf(cause, "no such session: %s", target);
918 			else
919 				xasprintf(cause, "no current session");
920 			return (OPTIONS_TABLE_NONE);
921 		}
922 		*oo = s->options;
923 		return (OPTIONS_TABLE_SESSION);
924 	}
925 }
926 
927 struct style *
options_string_to_style(struct options * oo,const char * name,struct format_tree * ft)928 options_string_to_style(struct options *oo, const char *name,
929     struct format_tree *ft)
930 {
931 	struct options_entry	*o;
932 	const char		*s;
933 	char			*expanded;
934 
935 	o = options_get(oo, name);
936 	if (o == NULL || !OPTIONS_IS_STRING(o))
937 		return (NULL);
938 
939 	if (o->cached)
940 		return (&o->style);
941 	s = o->value.string;
942 	log_debug("%s: %s is '%s'", __func__, name, s);
943 
944 	style_set(&o->style, &grid_default_cell);
945 	o->cached = (strstr(s, "#{") == NULL);
946 
947 	if (ft != NULL && !o->cached) {
948 		expanded = format_expand(ft, s);
949 		if (style_parse(&o->style, &grid_default_cell, expanded) != 0) {
950 			free(expanded);
951 			return (NULL);
952 		}
953 		free(expanded);
954 	} else {
955 		if (style_parse(&o->style, &grid_default_cell, s) != 0)
956 			return (NULL);
957 	}
958 	return (&o->style);
959 }
960 
961 static int
options_from_string_check(const struct options_table_entry * oe,const char * value,char ** cause)962 options_from_string_check(const struct options_table_entry *oe,
963     const char *value, char **cause)
964 {
965 	struct style	sy;
966 
967 	if (oe == NULL)
968 		return (0);
969 	if (strcmp(oe->name, "default-shell") == 0 && !checkshell(value)) {
970 		xasprintf(cause, "not a suitable shell: %s", value);
971 		return (-1);
972 	}
973 	if (oe->pattern != NULL && fnmatch(oe->pattern, value, 0) != 0) {
974 		xasprintf(cause, "value is invalid: %s", value);
975 		return (-1);
976 	}
977 	if ((oe->flags & OPTIONS_TABLE_IS_STYLE) &&
978 	    strstr(value, "#{") == NULL &&
979 	    style_parse(&sy, &grid_default_cell, value) != 0) {
980 		xasprintf(cause, "invalid style: %s", value);
981 		return (-1);
982 	}
983 	return (0);
984 }
985 
986 static int
options_from_string_flag(struct options * oo,const char * name,const char * value,char ** cause)987 options_from_string_flag(struct options *oo, const char *name,
988     const char *value, char **cause)
989 {
990 	int	flag;
991 
992 	if (value == NULL || *value == '\0')
993 		flag = !options_get_number(oo, name);
994 	else if (strcmp(value, "1") == 0 ||
995 	    strcasecmp(value, "on") == 0 ||
996 	    strcasecmp(value, "yes") == 0)
997 		flag = 1;
998 	else if (strcmp(value, "0") == 0 ||
999 	    strcasecmp(value, "off") == 0 ||
1000 	    strcasecmp(value, "no") == 0)
1001 		flag = 0;
1002 	else {
1003 		xasprintf(cause, "bad value: %s", value);
1004 		return (-1);
1005 	}
1006 	options_set_number(oo, name, flag);
1007 	return (0);
1008 }
1009 
1010 int
options_find_choice(const struct options_table_entry * oe,const char * value,char ** cause)1011 options_find_choice(const struct options_table_entry *oe, const char *value,
1012     char **cause)
1013 {
1014 	const char	**cp;
1015 	int		  n = 0, choice = -1;
1016 
1017 	for (cp = oe->choices; *cp != NULL; cp++) {
1018 		if (strcmp(*cp, value) == 0)
1019 			choice = n;
1020 		n++;
1021 	}
1022 	if (choice == -1) {
1023 		xasprintf(cause, "unknown value: %s", value);
1024 		return (-1);
1025 	}
1026 	return (choice);
1027 }
1028 
1029 static int
options_from_string_choice(const struct options_table_entry * oe,struct options * oo,const char * name,const char * value,char ** cause)1030 options_from_string_choice(const struct options_table_entry *oe,
1031     struct options *oo, const char *name, const char *value, char **cause)
1032 {
1033 	int	choice = -1;
1034 
1035 	if (value == NULL) {
1036 		choice = options_get_number(oo, name);
1037 		if (choice < 2)
1038 			choice = !choice;
1039 	} else {
1040 		choice = options_find_choice(oe, value, cause);
1041 		if (choice < 0)
1042 			return (-1);
1043 	}
1044 	options_set_number(oo, name, choice);
1045 	return (0);
1046 }
1047 
1048 int
options_from_string(struct options * oo,const struct options_table_entry * oe,const char * name,const char * value,int append,char ** cause)1049 options_from_string(struct options *oo, const struct options_table_entry *oe,
1050     const char *name, const char *value, int append, char **cause)
1051 {
1052 	enum options_table_type	 type;
1053 	long long		 number;
1054 	const char		*errstr, *new;
1055 	char			*old;
1056 	key_code		 key;
1057 
1058 	if (oe != NULL) {
1059 		if (value == NULL &&
1060 		    oe->type != OPTIONS_TABLE_FLAG &&
1061 		    oe->type != OPTIONS_TABLE_CHOICE) {
1062 			xasprintf(cause, "empty value");
1063 			return (-1);
1064 		}
1065 		type = oe->type;
1066 	} else {
1067 		if (*name != '@') {
1068 			xasprintf(cause, "bad option name");
1069 			return (-1);
1070 		}
1071 		type = OPTIONS_TABLE_STRING;
1072 	}
1073 
1074 	switch (type) {
1075 	case OPTIONS_TABLE_STRING:
1076 		old = xstrdup(options_get_string(oo, name));
1077 		options_set_string(oo, name, append, "%s", value);
1078 
1079 		new = options_get_string(oo, name);
1080 		if (options_from_string_check(oe, new, cause) != 0) {
1081 			options_set_string(oo, name, 0, "%s", old);
1082 			free(old);
1083 			return (-1);
1084 		}
1085 		free(old);
1086 		return (0);
1087 	case OPTIONS_TABLE_NUMBER:
1088 		number = strtonum(value, oe->minimum, oe->maximum, &errstr);
1089 		if (errstr != NULL) {
1090 			xasprintf(cause, "value is %s: %s", errstr, value);
1091 			return (-1);
1092 		}
1093 		options_set_number(oo, name, number);
1094 		return (0);
1095 	case OPTIONS_TABLE_KEY:
1096 		key = key_string_lookup_string(value);
1097 		if (key == KEYC_UNKNOWN) {
1098 			xasprintf(cause, "bad key: %s", value);
1099 			return (-1);
1100 		}
1101 		options_set_number(oo, name, key);
1102 		return (0);
1103 	case OPTIONS_TABLE_COLOUR:
1104 		if ((number = colour_fromstring(value)) == -1) {
1105 			xasprintf(cause, "bad colour: %s", value);
1106 			return (-1);
1107 		}
1108 		options_set_number(oo, name, number);
1109 		return (0);
1110 	case OPTIONS_TABLE_FLAG:
1111 		return (options_from_string_flag(oo, name, value, cause));
1112 	case OPTIONS_TABLE_CHOICE:
1113 		return (options_from_string_choice(oe, oo, name, value, cause));
1114 	case OPTIONS_TABLE_COMMAND:
1115 		break;
1116 	}
1117 	return (-1);
1118 }
1119 
1120 void
options_push_changes(const char * name)1121 options_push_changes(const char *name)
1122 {
1123 	struct client		*loop;
1124 	struct session		*s;
1125 	struct window		*w;
1126 	struct window_pane	*wp;
1127 
1128 	log_debug("%s: %s", __func__, name);
1129 
1130 	if (strcmp(name, "automatic-rename") == 0) {
1131 		RB_FOREACH(w, windows, &windows) {
1132 			if (w->active == NULL)
1133 				continue;
1134 			if (options_get_number(w->options, name))
1135 				w->active->flags |= PANE_CHANGED;
1136 		}
1137 	}
1138 	if (strcmp(name, "cursor-colour") == 0) {
1139 		RB_FOREACH(wp, window_pane_tree, &all_window_panes)
1140 			window_pane_default_cursor(wp);
1141 	}
1142 	if (strcmp(name, "cursor-style") == 0) {
1143 		RB_FOREACH(wp, window_pane_tree, &all_window_panes)
1144 			window_pane_default_cursor(wp);
1145 	}
1146 	if (strcmp(name, "fill-character") == 0) {
1147 		RB_FOREACH(w, windows, &windows)
1148 			window_set_fill_character(w);
1149 	}
1150 	if (strcmp(name, "key-table") == 0) {
1151 		TAILQ_FOREACH(loop, &clients, entry)
1152 			server_client_set_key_table(loop, NULL);
1153 	}
1154 	if (strcmp(name, "user-keys") == 0) {
1155 		TAILQ_FOREACH(loop, &clients, entry) {
1156 			if (loop->tty.flags & TTY_OPENED)
1157 				tty_keys_build(&loop->tty);
1158 		}
1159 	}
1160 	if (strcmp(name, "status") == 0 ||
1161 	    strcmp(name, "status-interval") == 0)
1162 		status_timer_start_all();
1163 	if (strcmp(name, "monitor-silence") == 0)
1164 		alerts_reset_all();
1165 	if (strcmp(name, "window-style") == 0 ||
1166 	    strcmp(name, "window-active-style") == 0) {
1167 		RB_FOREACH(wp, window_pane_tree, &all_window_panes)
1168 			wp->flags |= PANE_STYLECHANGED;
1169 	}
1170 	if (strcmp(name, "pane-colours") == 0) {
1171 		RB_FOREACH(wp, window_pane_tree, &all_window_panes)
1172 			colour_palette_from_option(&wp->palette, wp->options);
1173 	}
1174 	if (strcmp(name, "pane-border-status") == 0 ||
1175 	    strcmp(name, "pane-scrollbars") == 0 ||
1176 	    strcmp(name, "pane-scrollbars-position") == 0) {
1177 		RB_FOREACH(w, windows, &windows)
1178 			layout_fix_panes(w, NULL);
1179 	}
1180 	if (strcmp(name, "input-buffer-size") == 0)
1181 		input_set_buffer_size(options_get_number(global_options, name));
1182 	RB_FOREACH(s, sessions, &sessions)
1183 		status_update_cache(s);
1184 
1185 	recalculate_sizes();
1186 	TAILQ_FOREACH(loop, &clients, entry) {
1187 		if (loop->session != NULL)
1188 			server_redraw_client(loop);
1189 	}
1190 }
1191 
1192 int
options_remove_or_default(struct options_entry * o,int idx,char ** cause)1193 options_remove_or_default(struct options_entry *o, int idx, char **cause)
1194 {
1195 	struct options	*oo = o->owner;
1196 
1197 	if (idx == -1) {
1198 		if (o->tableentry != NULL &&
1199 		    (oo == global_options ||
1200 		    oo == global_s_options ||
1201 		    oo == global_w_options))
1202 			options_default(oo, o->tableentry);
1203 		else
1204 			options_remove(o);
1205 	} else if (options_array_set(o, idx, NULL, 0, cause) != 0)
1206 		return (-1);
1207 	return (0);
1208 }
1209