xref: /openbsd-src/usr.bin/tmux/options.c (revision 3d40d63a87e7d477e956d9dfca1e5d50688e719c)
1*3d40d63aSnicm /* $OpenBSD: options.c,v 1.74 2025/01/01 15:17:36 nicm Exp $ */
2311827fbSnicm 
3311827fbSnicm /*
498ca8272Snicm  * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
5311827fbSnicm  *
6311827fbSnicm  * Permission to use, copy, modify, and distribute this software for any
7311827fbSnicm  * purpose with or without fee is hereby granted, provided that the above
8311827fbSnicm  * copyright notice and this permission notice appear in all copies.
9311827fbSnicm  *
10311827fbSnicm  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11311827fbSnicm  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12311827fbSnicm  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13311827fbSnicm  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14311827fbSnicm  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15311827fbSnicm  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16311827fbSnicm  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17311827fbSnicm  */
18311827fbSnicm 
19311827fbSnicm #include <sys/types.h>
20311827fbSnicm 
2158eb4b5dSnicm #include <ctype.h>
2267c16a7cSnicm #include <fnmatch.h>
23311827fbSnicm #include <stdarg.h>
247d053cf9Snicm #include <stdlib.h>
25311827fbSnicm #include <string.h>
26311827fbSnicm 
27311827fbSnicm #include "tmux.h"
28311827fbSnicm 
29311827fbSnicm /*
30311827fbSnicm  * Option handling; each option has a name, type and value and is stored in
31a8e0565aSnicm  * a red-black tree.
32311827fbSnicm  */
33311827fbSnicm 
3439052edfSnicm struct options_array_item {
3539052edfSnicm 	u_int				 index;
3684306383Snicm 	union options_value		 value;
3739052edfSnicm 	RB_ENTRY(options_array_item)	 entry;
3839052edfSnicm };
3939052edfSnicm static int
4039052edfSnicm options_array_cmp(struct options_array_item *a1, struct options_array_item *a2)
4139052edfSnicm {
4239052edfSnicm 	if (a1->index < a2->index)
4339052edfSnicm 		return (-1);
4439052edfSnicm 	if (a1->index > a2->index)
4539052edfSnicm 		return (1);
4639052edfSnicm 	return (0);
4739052edfSnicm }
4839052edfSnicm RB_GENERATE_STATIC(options_array, options_array_item, entry, options_array_cmp);
4939052edfSnicm 
5058900c88Snicm struct options_entry {
5158eb4b5dSnicm 	struct options				*owner;
5258eb4b5dSnicm 
5358eb4b5dSnicm 	const char				*name;
5458eb4b5dSnicm 	const struct options_table_entry	*tableentry;
5584306383Snicm 	union options_value			 value;
5658eb4b5dSnicm 
5701c0c428Snicm 	int					 cached;
5801c0c428Snicm 	struct style				 style;
5901c0c428Snicm 
6058900c88Snicm 	RB_ENTRY(options_entry)			 entry;
6158eb4b5dSnicm };
6258eb4b5dSnicm 
63d89252e5Snicm struct options {
6458900c88Snicm 	RB_HEAD(options_tree, options_entry)	 tree;
65d89252e5Snicm 	struct options				*parent;
66d89252e5Snicm };
67d89252e5Snicm 
6858900c88Snicm static struct options_entry	*options_add(struct options *, const char *);
6994c0d63cSnicm static void			 options_remove(struct options_entry *);
7058eb4b5dSnicm 
7158eb4b5dSnicm #define OPTIONS_IS_STRING(o)						\
7258eb4b5dSnicm 	((o)->tableentry == NULL ||					\
7358eb4b5dSnicm 	    (o)->tableentry->type == OPTIONS_TABLE_STRING)
7458eb4b5dSnicm #define OPTIONS_IS_NUMBER(o) \
7558eb4b5dSnicm 	((o)->tableentry != NULL &&					\
7658eb4b5dSnicm 	    ((o)->tableentry->type == OPTIONS_TABLE_NUMBER ||		\
7758eb4b5dSnicm 	    (o)->tableentry->type == OPTIONS_TABLE_KEY ||		\
7858eb4b5dSnicm 	    (o)->tableentry->type == OPTIONS_TABLE_COLOUR ||		\
7958eb4b5dSnicm 	    (o)->tableentry->type == OPTIONS_TABLE_FLAG ||		\
8058eb4b5dSnicm 	    (o)->tableentry->type == OPTIONS_TABLE_CHOICE))
81844b9093Snicm #define OPTIONS_IS_COMMAND(o) \
82844b9093Snicm 	((o)->tableentry != NULL &&					\
83844b9093Snicm 	    (o)->tableentry->type == OPTIONS_TABLE_COMMAND)
8484306383Snicm 
8558eb4b5dSnicm #define OPTIONS_IS_ARRAY(o)						\
8658eb4b5dSnicm 	((o)->tableentry != NULL &&					\
8784306383Snicm 	    ((o)->tableentry->flags & OPTIONS_TABLE_IS_ARRAY))
8858eb4b5dSnicm 
8958900c88Snicm static int	options_cmp(struct options_entry *, struct options_entry *);
9058900c88Snicm RB_GENERATE_STATIC(options_tree, options_entry, entry, options_cmp);
91311827fbSnicm 
92483d515fSnicm static int
9358900c88Snicm options_cmp(struct options_entry *lhs, struct options_entry *rhs)
94311827fbSnicm {
9558eb4b5dSnicm 	return (strcmp(lhs->name, rhs->name));
9658eb4b5dSnicm }
9758eb4b5dSnicm 
9843dc5f42Snicm static const char *
9943dc5f42Snicm options_map_name(const char *name)
10043dc5f42Snicm {
10143dc5f42Snicm 	const struct options_name_map	*map;
10243dc5f42Snicm 
10343dc5f42Snicm 	for (map = options_other_names; map->from != NULL; map++) {
10443dc5f42Snicm 		if (strcmp(map->from, name) == 0)
10543dc5f42Snicm 			return (map->to);
10643dc5f42Snicm 	}
10743dc5f42Snicm 	return (name);
10843dc5f42Snicm }
10943dc5f42Snicm 
11058eb4b5dSnicm static const struct options_table_entry *
11158eb4b5dSnicm options_parent_table_entry(struct options *oo, const char *s)
11258eb4b5dSnicm {
11358900c88Snicm 	struct options_entry	*o;
11458eb4b5dSnicm 
11558eb4b5dSnicm 	if (oo->parent == NULL)
11658eb4b5dSnicm 		fatalx("no parent options for %s", s);
1176e0f28f8Snicm 	o = options_get(oo->parent, s);
11858eb4b5dSnicm 	if (o == NULL)
11958eb4b5dSnicm 		fatalx("%s not in parent options", s);
12058eb4b5dSnicm 	return (o->tableentry);
121311827fbSnicm }
122311827fbSnicm 
12384306383Snicm static void
12484306383Snicm options_value_free(struct options_entry *o, union options_value *ov)
12584306383Snicm {
12684306383Snicm 	if (OPTIONS_IS_STRING(o))
12784306383Snicm 		free(ov->string);
128844b9093Snicm 	if (OPTIONS_IS_COMMAND(o) && ov->cmdlist != NULL)
129844b9093Snicm 		cmd_list_free(ov->cmdlist);
13084306383Snicm }
13184306383Snicm 
13211897870Snicm static char *
13367c16a7cSnicm options_value_to_string(struct options_entry *o, union options_value *ov,
13484306383Snicm     int numeric)
13584306383Snicm {
13611897870Snicm 	char	*s;
13784306383Snicm 
138844b9093Snicm 	if (OPTIONS_IS_COMMAND(o))
1395c131106Snicm 		return (cmd_list_print(ov->cmdlist, 0));
14084306383Snicm 	if (OPTIONS_IS_NUMBER(o)) {
14184306383Snicm 		switch (o->tableentry->type) {
14284306383Snicm 		case OPTIONS_TABLE_NUMBER:
14311897870Snicm 			xasprintf(&s, "%lld", ov->number);
14484306383Snicm 			break;
14584306383Snicm 		case OPTIONS_TABLE_KEY:
1465416581eSnicm 			s = xstrdup(key_string_lookup_key(ov->number, 0));
14784306383Snicm 			break;
14884306383Snicm 		case OPTIONS_TABLE_COLOUR:
14911897870Snicm 			s = xstrdup(colour_tostring(ov->number));
15084306383Snicm 			break;
15184306383Snicm 		case OPTIONS_TABLE_FLAG:
15284306383Snicm 			if (numeric)
15311897870Snicm 				xasprintf(&s, "%lld", ov->number);
15484306383Snicm 			else
15511897870Snicm 				s = xstrdup(ov->number ? "on" : "off");
15684306383Snicm 			break;
15784306383Snicm 		case OPTIONS_TABLE_CHOICE:
15811897870Snicm 			s = xstrdup(o->tableentry->choices[ov->number]);
15984306383Snicm 			break;
1608f1cd816Snicm 		default:
16111897870Snicm 			fatalx("not a number option type");
16284306383Snicm 		}
16384306383Snicm 		return (s);
16484306383Snicm 	}
16584306383Snicm 	if (OPTIONS_IS_STRING(o))
16611897870Snicm 		return (xstrdup(ov->string));
16711897870Snicm 	return (xstrdup(""));
16884306383Snicm }
16984306383Snicm 
170d89252e5Snicm struct options *
171d89252e5Snicm options_create(struct options *parent)
172311827fbSnicm {
173d89252e5Snicm 	struct options	*oo;
174d89252e5Snicm 
175d89252e5Snicm 	oo = xcalloc(1, sizeof *oo);
176574ef2a1Snicm 	RB_INIT(&oo->tree);
177311827fbSnicm 	oo->parent = parent;
178d89252e5Snicm 	return (oo);
179311827fbSnicm }
180311827fbSnicm 
181483d515fSnicm void
182483d515fSnicm options_free(struct options *oo)
183483d515fSnicm {
18458900c88Snicm 	struct options_entry	*o, *tmp;
185483d515fSnicm 
18658eb4b5dSnicm 	RB_FOREACH_SAFE(o, options_tree, &oo->tree, tmp)
18758eb4b5dSnicm 		options_remove(o);
188d89252e5Snicm 	free(oo);
189d89252e5Snicm }
190d89252e5Snicm 
19167c16a7cSnicm struct options *
19267c16a7cSnicm options_get_parent(struct options *oo)
19367c16a7cSnicm {
19467c16a7cSnicm 	return (oo->parent);
19567c16a7cSnicm }
19667c16a7cSnicm 
1976e0f28f8Snicm void
1986e0f28f8Snicm options_set_parent(struct options *oo, struct options *parent)
1996e0f28f8Snicm {
2006e0f28f8Snicm 	oo->parent = parent;
2016e0f28f8Snicm }
2026e0f28f8Snicm 
20358900c88Snicm struct options_entry *
204d89252e5Snicm options_first(struct options *oo)
205d89252e5Snicm {
206d89252e5Snicm 	return (RB_MIN(options_tree, &oo->tree));
207d89252e5Snicm }
208d89252e5Snicm 
20958900c88Snicm struct options_entry *
21058900c88Snicm options_next(struct options_entry *o)
211d89252e5Snicm {
212d89252e5Snicm 	return (RB_NEXT(options_tree, &oo->tree, o));
213311827fbSnicm }
214311827fbSnicm 
21558900c88Snicm struct options_entry *
21658eb4b5dSnicm options_get_only(struct options *oo, const char *name)
217311827fbSnicm {
21843dc5f42Snicm 	struct options_entry	o = { .name = name }, *found;
219311827fbSnicm 
22043dc5f42Snicm 	found = RB_FIND(options_tree, &oo->tree, &o);
22143dc5f42Snicm 	if (found == NULL) {
22243dc5f42Snicm 		o.name = options_map_name(name);
22358eb4b5dSnicm 		return (RB_FIND(options_tree, &oo->tree, &o));
224311827fbSnicm 	}
22543dc5f42Snicm 	return (found);
22643dc5f42Snicm }
227311827fbSnicm 
22858900c88Snicm struct options_entry *
22958eb4b5dSnicm options_get(struct options *oo, const char *name)
230311827fbSnicm {
23158900c88Snicm 	struct options_entry	*o;
232311827fbSnicm 
23358eb4b5dSnicm 	o = options_get_only(oo, name);
234311827fbSnicm 	while (o == NULL) {
235311827fbSnicm 		oo = oo->parent;
236311827fbSnicm 		if (oo == NULL)
237311827fbSnicm 			break;
23858eb4b5dSnicm 		o = options_get_only(oo, name);
239311827fbSnicm 	}
240311827fbSnicm 	return (o);
241311827fbSnicm }
242311827fbSnicm 
24358900c88Snicm struct options_entry *
24458eb4b5dSnicm options_empty(struct options *oo, const struct options_table_entry *oe)
245311827fbSnicm {
24658900c88Snicm 	struct options_entry	*o;
247311827fbSnicm 
24858eb4b5dSnicm 	o = options_add(oo, oe->name);
24958eb4b5dSnicm 	o->tableentry = oe;
25058eb4b5dSnicm 
25111897870Snicm 	if (oe->flags & OPTIONS_TABLE_IS_ARRAY)
25284306383Snicm 		RB_INIT(&o->value.array);
25339052edfSnicm 
25458eb4b5dSnicm 	return (o);
255311827fbSnicm }
256311827fbSnicm 
25758900c88Snicm struct options_entry *
25858eb4b5dSnicm options_default(struct options *oo, const struct options_table_entry *oe)
25958eb4b5dSnicm {
26058900c88Snicm 	struct options_entry	*o;
26184306383Snicm 	union options_value	*ov;
262d1d49da9Snicm 	u_int			 i;
26358eb4b5dSnicm 
26458eb4b5dSnicm 	o = options_empty(oo, oe);
26584306383Snicm 	ov = &o->value;
26684306383Snicm 
26784306383Snicm 	if (oe->flags & OPTIONS_TABLE_IS_ARRAY) {
268844b9093Snicm 		if (oe->default_arr == NULL) {
269844b9093Snicm 			options_array_assign(o, oe->default_str, NULL);
270844b9093Snicm 			return (o);
271844b9093Snicm 		}
272d1d49da9Snicm 		for (i = 0; oe->default_arr[i] != NULL; i++)
273844b9093Snicm 			options_array_set(o, i, oe->default_arr[i], 0, NULL);
27484306383Snicm 		return (o);
27584306383Snicm 	}
27684306383Snicm 
27784306383Snicm 	switch (oe->type) {
27884306383Snicm 	case OPTIONS_TABLE_STRING:
27984306383Snicm 		ov->string = xstrdup(oe->default_str);
28084306383Snicm 		break;
28184306383Snicm 	default:
28284306383Snicm 		ov->number = oe->default_num;
28384306383Snicm 		break;
28484306383Snicm 	}
28558eb4b5dSnicm 	return (o);
28658eb4b5dSnicm }
28758eb4b5dSnicm 
28867c16a7cSnicm char *
28967c16a7cSnicm options_default_to_string(const struct options_table_entry *oe)
29067c16a7cSnicm {
29167c16a7cSnicm 	char	*s;
29267c16a7cSnicm 
29367c16a7cSnicm 	switch (oe->type) {
29467c16a7cSnicm 	case OPTIONS_TABLE_STRING:
29567c16a7cSnicm 	case OPTIONS_TABLE_COMMAND:
29667c16a7cSnicm 		s = xstrdup(oe->default_str);
29767c16a7cSnicm 		break;
29867c16a7cSnicm 	case OPTIONS_TABLE_NUMBER:
29967c16a7cSnicm 		xasprintf(&s, "%lld", oe->default_num);
30067c16a7cSnicm 		break;
30167c16a7cSnicm 	case OPTIONS_TABLE_KEY:
3025416581eSnicm 		s = xstrdup(key_string_lookup_key(oe->default_num, 0));
30367c16a7cSnicm 		break;
30467c16a7cSnicm 	case OPTIONS_TABLE_COLOUR:
30567c16a7cSnicm 		s = xstrdup(colour_tostring(oe->default_num));
30667c16a7cSnicm 		break;
30767c16a7cSnicm 	case OPTIONS_TABLE_FLAG:
30867c16a7cSnicm 		s = xstrdup(oe->default_num ? "on" : "off");
30967c16a7cSnicm 		break;
31067c16a7cSnicm 	case OPTIONS_TABLE_CHOICE:
31167c16a7cSnicm 		s = xstrdup(oe->choices[oe->default_num]);
31267c16a7cSnicm 		break;
3138f1cd816Snicm 	default:
3148f1cd816Snicm 		fatalx("unknown option type");
31567c16a7cSnicm 	}
31667c16a7cSnicm 	return (s);
31767c16a7cSnicm }
31867c16a7cSnicm 
31958900c88Snicm static struct options_entry *
32058eb4b5dSnicm options_add(struct options *oo, const char *name)
32158eb4b5dSnicm {
32258900c88Snicm 	struct options_entry	*o;
32358eb4b5dSnicm 
32458eb4b5dSnicm 	o = options_get_only(oo, name);
32558eb4b5dSnicm 	if (o != NULL)
32658eb4b5dSnicm 		options_remove(o);
32758eb4b5dSnicm 
32858eb4b5dSnicm 	o = xcalloc(1, sizeof *o);
32958eb4b5dSnicm 	o->owner = oo;
33058eb4b5dSnicm 	o->name = xstrdup(name);
33158eb4b5dSnicm 
33258eb4b5dSnicm 	RB_INSERT(options_tree, &oo->tree, o);
33358eb4b5dSnicm 	return (o);
33458eb4b5dSnicm }
33558eb4b5dSnicm 
33694c0d63cSnicm static void
33758900c88Snicm options_remove(struct options_entry *o)
33858eb4b5dSnicm {
33958eb4b5dSnicm 	struct options	*oo = o->owner;
34058eb4b5dSnicm 
34184306383Snicm 	if (OPTIONS_IS_ARRAY(o))
34239052edfSnicm 		options_array_clear(o);
34384306383Snicm 	else
34484306383Snicm 		options_value_free(o, &o->value);
34558eb4b5dSnicm 	RB_REMOVE(options_tree, &oo->tree, o);
346d09880d0Snicm 	free((void *)o->name);
34758eb4b5dSnicm 	free(o);
34858eb4b5dSnicm }
34958eb4b5dSnicm 
35058eb4b5dSnicm const char *
35158900c88Snicm options_name(struct options_entry *o)
35258eb4b5dSnicm {
35358eb4b5dSnicm 	return (o->name);
35458eb4b5dSnicm }
35558eb4b5dSnicm 
35667c16a7cSnicm struct options *
35767c16a7cSnicm options_owner(struct options_entry *o)
35867c16a7cSnicm {
35967c16a7cSnicm 	return (o->owner);
36067c16a7cSnicm }
36167c16a7cSnicm 
36258eb4b5dSnicm const struct options_table_entry *
36358900c88Snicm options_table_entry(struct options_entry *o)
36458eb4b5dSnicm {
36558eb4b5dSnicm 	return (o->tableentry);
36658eb4b5dSnicm }
36758eb4b5dSnicm 
36839052edfSnicm static struct options_array_item *
36939052edfSnicm options_array_item(struct options_entry *o, u_int idx)
37039052edfSnicm {
37139052edfSnicm 	struct options_array_item	a;
37239052edfSnicm 
37339052edfSnicm 	a.index = idx;
37484306383Snicm 	return (RB_FIND(options_array, &o->value.array, &a));
37539052edfSnicm }
37639052edfSnicm 
37776e38b2dSnicm static struct options_array_item *
37876e38b2dSnicm options_array_new(struct options_entry *o, u_int idx)
37976e38b2dSnicm {
38076e38b2dSnicm 	struct options_array_item	*a;
38176e38b2dSnicm 
38276e38b2dSnicm 	a = xcalloc(1, sizeof *a);
38376e38b2dSnicm 	a->index = idx;
38476e38b2dSnicm 	RB_INSERT(options_array, &o->value.array, a);
38576e38b2dSnicm 	return (a);
38676e38b2dSnicm }
38776e38b2dSnicm 
38839052edfSnicm static void
38939052edfSnicm options_array_free(struct options_entry *o, struct options_array_item *a)
39039052edfSnicm {
39184306383Snicm 	options_value_free(o, &a->value);
39284306383Snicm 	RB_REMOVE(options_array, &o->value.array, a);
39339052edfSnicm 	free(a);
39439052edfSnicm }
39539052edfSnicm 
3962f0d274aSnicm void
3972f0d274aSnicm options_array_clear(struct options_entry *o)
3982f0d274aSnicm {
39939052edfSnicm 	struct options_array_item	*a, *a1;
40039052edfSnicm 
40139052edfSnicm 	if (!OPTIONS_IS_ARRAY(o))
40239052edfSnicm 		return;
40339052edfSnicm 
40484306383Snicm 	RB_FOREACH_SAFE(a, options_array, &o->value.array, a1)
40539052edfSnicm 		options_array_free(o, a);
4062f0d274aSnicm }
4072f0d274aSnicm 
40884306383Snicm union options_value *
40958900c88Snicm options_array_get(struct options_entry *o, u_int idx)
41058eb4b5dSnicm {
41139052edfSnicm 	struct options_array_item	*a;
41239052edfSnicm 
41358eb4b5dSnicm 	if (!OPTIONS_IS_ARRAY(o))
41458eb4b5dSnicm 		return (NULL);
41539052edfSnicm 	a = options_array_item(o, idx);
41639052edfSnicm 	if (a == NULL)
41758eb4b5dSnicm 		return (NULL);
41884306383Snicm 	return (&a->value);
41958eb4b5dSnicm }
42058eb4b5dSnicm 
42158eb4b5dSnicm int
4222f0d274aSnicm options_array_set(struct options_entry *o, u_int idx, const char *value,
423844b9093Snicm     int append, char **cause)
42458eb4b5dSnicm {
42539052edfSnicm 	struct options_array_item	*a;
4262f0d274aSnicm 	char				*new;
427df6ab229Snicm 	struct cmd_parse_result		*pr;
42833a1e283Snicm 	long long		 	 number;
42958eb4b5dSnicm 
430844b9093Snicm 	if (!OPTIONS_IS_ARRAY(o)) {
431b68dd44eSnicm 		if (cause != NULL)
432844b9093Snicm 			*cause = xstrdup("not an array");
43358eb4b5dSnicm 		return (-1);
434844b9093Snicm 	}
435844b9093Snicm 
43676e38b2dSnicm 	if (value == NULL) {
43776e38b2dSnicm 		a = options_array_item(o, idx);
43876e38b2dSnicm 		if (a != NULL)
43976e38b2dSnicm 			options_array_free(o, a);
44076e38b2dSnicm 		return (0);
44176e38b2dSnicm 	}
44276e38b2dSnicm 
44376e38b2dSnicm 	if (OPTIONS_IS_COMMAND(o)) {
444df6ab229Snicm 		pr = cmd_parse_from_string(value, NULL);
445df6ab229Snicm 		switch (pr->status) {
446df6ab229Snicm 		case CMD_PARSE_ERROR:
447df6ab229Snicm 			if (cause != NULL)
448df6ab229Snicm 				*cause = pr->error;
449df6ab229Snicm 			else
450df6ab229Snicm 				free(pr->error);
451df6ab229Snicm 			return (-1);
452df6ab229Snicm 		case CMD_PARSE_SUCCESS:
453df6ab229Snicm 			break;
454844b9093Snicm 		}
45558eb4b5dSnicm 
45639052edfSnicm 		a = options_array_item(o, idx);
45776e38b2dSnicm 		if (a == NULL)
45876e38b2dSnicm 			a = options_array_new(o, idx);
45976e38b2dSnicm 		else
46076e38b2dSnicm 			options_value_free(o, &a->value);
46176e38b2dSnicm 		a->value.cmdlist = pr->cmdlist;
46258eb4b5dSnicm 		return (0);
46358eb4b5dSnicm 	}
46458eb4b5dSnicm 
465844b9093Snicm 	if (OPTIONS_IS_STRING(o)) {
46676e38b2dSnicm 		a = options_array_item(o, idx);
46739052edfSnicm 		if (a != NULL && append)
46884306383Snicm 			xasprintf(&new, "%s%s", a->value.string, value);
46939052edfSnicm 		else
47039052edfSnicm 			new = xstrdup(value);
47176e38b2dSnicm 		if (a == NULL)
47276e38b2dSnicm 			a = options_array_new(o, idx);
47376e38b2dSnicm 		else
47476e38b2dSnicm 			options_value_free(o, &a->value);
47576e38b2dSnicm 		a->value.string = new;
47676e38b2dSnicm 		return (0);
47739052edfSnicm 	}
47839052edfSnicm 
47933a1e283Snicm 	if (o->tableentry->type == OPTIONS_TABLE_COLOUR) {
48033a1e283Snicm 		if ((number = colour_fromstring(value)) == -1) {
48133a1e283Snicm 			xasprintf(cause, "bad colour: %s", value);
48233a1e283Snicm 			return (-1);
48333a1e283Snicm 		}
48433a1e283Snicm 		a = options_array_item(o, idx);
48533a1e283Snicm 		if (a == NULL)
48633a1e283Snicm 			a = options_array_new(o, idx);
48733a1e283Snicm 		else
48833a1e283Snicm 			options_value_free(o, &a->value);
48933a1e283Snicm 		a->value.number = number;
49033a1e283Snicm 		return (0);
49133a1e283Snicm 	}
49233a1e283Snicm 
49376e38b2dSnicm 	if (cause != NULL)
49476e38b2dSnicm 		*cause = xstrdup("wrong array type");
49576e38b2dSnicm 	return (-1);
49658eb4b5dSnicm }
49758eb4b5dSnicm 
498844b9093Snicm int
499844b9093Snicm options_array_assign(struct options_entry *o, const char *s, char **cause)
5002f0d274aSnicm {
5012f0d274aSnicm 	const char	*separator;
5022f0d274aSnicm 	char		*copy, *next, *string;
5032f0d274aSnicm 	u_int		 i;
5042f0d274aSnicm 
5052f0d274aSnicm 	separator = o->tableentry->separator;
5062f0d274aSnicm 	if (separator == NULL)
5072f0d274aSnicm 		separator = " ,";
508844b9093Snicm 	if (*separator == '\0') {
509844b9093Snicm 		if (*s == '\0')
510844b9093Snicm 			return (0);
511844b9093Snicm 		for (i = 0; i < UINT_MAX; i++) {
512844b9093Snicm 			if (options_array_item(o, i) == NULL)
513844b9093Snicm 				break;
514844b9093Snicm 		}
515844b9093Snicm 		return (options_array_set(o, i, s, 0, cause));
516844b9093Snicm 	}
5172f0d274aSnicm 
518844b9093Snicm 	if (*s == '\0')
519844b9093Snicm 		return (0);
5202f0d274aSnicm 	copy = string = xstrdup(s);
5212f0d274aSnicm 	while ((next = strsep(&string, separator)) != NULL) {
5222f0d274aSnicm 		if (*next == '\0')
5232f0d274aSnicm 			continue;
52439052edfSnicm 		for (i = 0; i < UINT_MAX; i++) {
52539052edfSnicm 			if (options_array_item(o, i) == NULL)
5262f0d274aSnicm 				break;
5272f0d274aSnicm 		}
52839052edfSnicm 		if (i == UINT_MAX)
5292f0d274aSnicm 			break;
530844b9093Snicm 		if (options_array_set(o, i, next, 0, cause) != 0) {
531844b9093Snicm 			free(copy);
532844b9093Snicm 			return (-1);
533844b9093Snicm 		}
5342f0d274aSnicm 	}
5352f0d274aSnicm 	free(copy);
536844b9093Snicm 	return (0);
5372f0d274aSnicm }
5382f0d274aSnicm 
53939052edfSnicm struct options_array_item *
54039052edfSnicm options_array_first(struct options_entry *o)
54139052edfSnicm {
54239052edfSnicm 	if (!OPTIONS_IS_ARRAY(o))
54339052edfSnicm 		return (NULL);
54484306383Snicm 	return (RB_MIN(options_array, &o->value.array));
54539052edfSnicm }
54639052edfSnicm 
54739052edfSnicm struct options_array_item *
54839052edfSnicm options_array_next(struct options_array_item *a)
54939052edfSnicm {
55084306383Snicm 	return (RB_NEXT(options_array, &o->value.array, a));
55139052edfSnicm }
55239052edfSnicm 
55339052edfSnicm u_int
55439052edfSnicm options_array_item_index(struct options_array_item *a)
55539052edfSnicm {
55639052edfSnicm 	return (a->index);
55739052edfSnicm }
55839052edfSnicm 
55984306383Snicm union options_value *
56039052edfSnicm options_array_item_value(struct options_array_item *a)
56139052edfSnicm {
56284306383Snicm 	return (&a->value);
56339052edfSnicm }
56439052edfSnicm 
56539052edfSnicm int
56667c16a7cSnicm options_is_array(struct options_entry *o)
56739052edfSnicm {
56839052edfSnicm 	return (OPTIONS_IS_ARRAY(o));
56939052edfSnicm }
57039052edfSnicm 
57158eb4b5dSnicm int
57267c16a7cSnicm options_is_string(struct options_entry *o)
57358eb4b5dSnicm {
57484306383Snicm 	return (OPTIONS_IS_STRING(o));
57558eb4b5dSnicm }
57658eb4b5dSnicm 
57711897870Snicm char *
57867c16a7cSnicm options_to_string(struct options_entry *o, int idx, int numeric)
57958eb4b5dSnicm {
58039052edfSnicm 	struct options_array_item	*a;
58150deb69bSnicm 	char				*result = NULL;
58250deb69bSnicm 	char				*last = NULL;
58350deb69bSnicm 	char				*next;
58458eb4b5dSnicm 
58558eb4b5dSnicm 	if (OPTIONS_IS_ARRAY(o)) {
58650deb69bSnicm 		if (idx == -1) {
58750deb69bSnicm 			RB_FOREACH(a, options_array, &o->value.array) {
58850deb69bSnicm 				next = options_value_to_string(o, &a->value,
58950deb69bSnicm 				    numeric);
59050deb69bSnicm 				if (last == NULL)
59150deb69bSnicm 					result = next;
59250deb69bSnicm 				else {
59350deb69bSnicm 					xasprintf(&result, "%s %s", last, next);
59450deb69bSnicm 					free(last);
59550deb69bSnicm 					free(next);
59650deb69bSnicm 				}
59750deb69bSnicm 				last = result;
59850deb69bSnicm 			}
59950deb69bSnicm 			if (result == NULL)
60011897870Snicm 				return (xstrdup(""));
60150deb69bSnicm 			return (result);
60250deb69bSnicm 		}
60339052edfSnicm 		a = options_array_item(o, idx);
60439052edfSnicm 		if (a == NULL)
60511897870Snicm 			return (xstrdup(""));
60667c16a7cSnicm 		return (options_value_to_string(o, &a->value, numeric));
60758eb4b5dSnicm 	}
60867c16a7cSnicm 	return (options_value_to_string(o, &o->value, numeric));
60958eb4b5dSnicm }
61058eb4b5dSnicm 
61158eb4b5dSnicm char *
61258eb4b5dSnicm options_parse(const char *name, int *idx)
61358eb4b5dSnicm {
61458eb4b5dSnicm 	char	*copy, *cp, *end;
61558eb4b5dSnicm 
61658eb4b5dSnicm 	if (*name == '\0')
61758eb4b5dSnicm 		return (NULL);
61858eb4b5dSnicm 	copy = xstrdup(name);
61958eb4b5dSnicm 	if ((cp = strchr(copy, '[')) == NULL) {
62058eb4b5dSnicm 		*idx = -1;
62158eb4b5dSnicm 		return (copy);
62258eb4b5dSnicm 	}
62358eb4b5dSnicm 	end = strchr(cp + 1, ']');
62458eb4b5dSnicm 	if (end == NULL || end[1] != '\0' || !isdigit((u_char)end[-1])) {
62558eb4b5dSnicm 		free(copy);
62658eb4b5dSnicm 		return (NULL);
62758eb4b5dSnicm 	}
62858eb4b5dSnicm 	if (sscanf(cp, "[%d]", idx) != 1 || *idx < 0) {
62958eb4b5dSnicm 		free(copy);
63058eb4b5dSnicm 		return (NULL);
63158eb4b5dSnicm 	}
63258eb4b5dSnicm 	*cp = '\0';
63358eb4b5dSnicm 	return (copy);
63458eb4b5dSnicm }
63558eb4b5dSnicm 
63658900c88Snicm struct options_entry *
63758eb4b5dSnicm options_parse_get(struct options *oo, const char *s, int *idx, int only)
63858eb4b5dSnicm {
63958900c88Snicm 	struct options_entry	*o;
64058eb4b5dSnicm 	char			*name;
64158eb4b5dSnicm 
64258eb4b5dSnicm 	name = options_parse(s, idx);
64358eb4b5dSnicm 	if (name == NULL)
64458eb4b5dSnicm 		return (NULL);
64558eb4b5dSnicm 	if (only)
64658eb4b5dSnicm 		o = options_get_only(oo, name);
64758eb4b5dSnicm 	else
64858eb4b5dSnicm 		o = options_get(oo, name);
64958eb4b5dSnicm 	free(name);
65058eb4b5dSnicm 	return (o);
65158eb4b5dSnicm }
65258eb4b5dSnicm 
65358eb4b5dSnicm char *
65458eb4b5dSnicm options_match(const char *s, int *idx, int *ambiguous)
65558eb4b5dSnicm {
65658eb4b5dSnicm 	const struct options_table_entry	*oe, *found;
65743dc5f42Snicm 	char					*parsed;
65843dc5f42Snicm 	const char				*name;
65958eb4b5dSnicm 	size_t					 namelen;
66058eb4b5dSnicm 
66143dc5f42Snicm 	parsed = options_parse(s, idx);
66243dc5f42Snicm 	if (parsed == NULL)
663cb007a1aSnicm 		return (NULL);
66443dc5f42Snicm 	if (*parsed == '@') {
665351a5613Snicm 		*ambiguous = 0;
66643dc5f42Snicm 		return (parsed);
667351a5613Snicm 	}
668351a5613Snicm 
66943dc5f42Snicm 	name = options_map_name(parsed);
67043dc5f42Snicm 	namelen = strlen(name);
67143dc5f42Snicm 
67258eb4b5dSnicm 	found = NULL;
67358eb4b5dSnicm 	for (oe = options_table; oe->name != NULL; oe++) {
67458eb4b5dSnicm 		if (strcmp(oe->name, name) == 0) {
67558eb4b5dSnicm 			found = oe;
67658eb4b5dSnicm 			break;
67758eb4b5dSnicm 		}
67858eb4b5dSnicm 		if (strncmp(oe->name, name, namelen) == 0) {
67958eb4b5dSnicm 			if (found != NULL) {
68058eb4b5dSnicm 				*ambiguous = 1;
68143dc5f42Snicm 				free(parsed);
68258eb4b5dSnicm 				return (NULL);
68358eb4b5dSnicm 			}
68458eb4b5dSnicm 			found = oe;
68558eb4b5dSnicm 		}
68658eb4b5dSnicm 	}
68743dc5f42Snicm 	free(parsed);
68858eb4b5dSnicm 	if (found == NULL) {
68958eb4b5dSnicm 		*ambiguous = 0;
69058eb4b5dSnicm 		return (NULL);
69158eb4b5dSnicm 	}
69258eb4b5dSnicm 	return (xstrdup(found->name));
69358eb4b5dSnicm }
69458eb4b5dSnicm 
69558900c88Snicm struct options_entry *
69658eb4b5dSnicm options_match_get(struct options *oo, const char *s, int *idx, int only,
69758eb4b5dSnicm     int *ambiguous)
69858eb4b5dSnicm {
69958eb4b5dSnicm 	char			*name;
70058900c88Snicm 	struct options_entry	*o;
70158eb4b5dSnicm 
70258eb4b5dSnicm 	name = options_match(s, idx, ambiguous);
70358eb4b5dSnicm 	if (name == NULL)
70458eb4b5dSnicm 		return (NULL);
70558eb4b5dSnicm 	*ambiguous = 0;
70658eb4b5dSnicm 	if (only)
70758eb4b5dSnicm 		o = options_get_only(oo, name);
70858eb4b5dSnicm 	else
70958eb4b5dSnicm 		o = options_get(oo, name);
71058eb4b5dSnicm 	free(name);
71158eb4b5dSnicm 	return (o);
71258eb4b5dSnicm }
71358eb4b5dSnicm 
71458eb4b5dSnicm const char *
71558eb4b5dSnicm options_get_string(struct options *oo, const char *name)
71658eb4b5dSnicm {
71758900c88Snicm 	struct options_entry	*o;
71858eb4b5dSnicm 
71958eb4b5dSnicm 	o = options_get(oo, name);
72058eb4b5dSnicm 	if (o == NULL)
72158eb4b5dSnicm 		fatalx("missing option %s", name);
72258eb4b5dSnicm 	if (!OPTIONS_IS_STRING(o))
72358eb4b5dSnicm 		fatalx("option %s is not a string", name);
72484306383Snicm 	return (o->value.string);
72558eb4b5dSnicm }
72658eb4b5dSnicm 
72758eb4b5dSnicm long long
72858eb4b5dSnicm options_get_number(struct options *oo, const char *name)
72958eb4b5dSnicm {
73058900c88Snicm 	struct options_entry	*o;
73158eb4b5dSnicm 
73258eb4b5dSnicm 	o = options_get(oo, name);
73358eb4b5dSnicm 	if (o == NULL)
73458eb4b5dSnicm 		fatalx("missing option %s", name);
73558eb4b5dSnicm 	if (!OPTIONS_IS_NUMBER(o))
73658eb4b5dSnicm 		fatalx("option %s is not a number", name);
73784306383Snicm 	return (o->value.number);
73858eb4b5dSnicm }
73958eb4b5dSnicm 
74058900c88Snicm struct options_entry *
741a12989f1Snicm options_set_string(struct options *oo, const char *name, int append,
742a12989f1Snicm     const char *fmt, ...)
743311827fbSnicm {
74458900c88Snicm 	struct options_entry	*o;
745311827fbSnicm 	va_list			 ap;
74601c0c428Snicm 	const char		*separator = "";
747a12989f1Snicm 	char			*s, *value;
748311827fbSnicm 
749311827fbSnicm 	va_start(ap, fmt);
750a12989f1Snicm 	xvasprintf(&s, fmt, ap);
75182b5cde7Snicm 	va_end(ap);
752a12989f1Snicm 
75358eb4b5dSnicm 	o = options_get_only(oo, name);
75458eb4b5dSnicm 	if (o != NULL && append && OPTIONS_IS_STRING(o)) {
75501c0c428Snicm 		if (*name != '@') {
75601c0c428Snicm 			separator = o->tableentry->separator;
75701c0c428Snicm 			if (separator == NULL)
75801c0c428Snicm 				separator = "";
75901c0c428Snicm 		}
76001c0c428Snicm 		xasprintf(&value, "%s%s%s", o->value.string, separator, s);
761a12989f1Snicm 		free(s);
76258eb4b5dSnicm 	} else
76358eb4b5dSnicm 		value = s;
76458eb4b5dSnicm 	if (o == NULL && *name == '@')
76558eb4b5dSnicm 		o = options_add(oo, name);
76658eb4b5dSnicm 	else if (o == NULL) {
76758eb4b5dSnicm 		o = options_default(oo, options_parent_table_entry(oo, name));
76858eb4b5dSnicm 		if (o == NULL)
76958eb4b5dSnicm 			return (NULL);
770a12989f1Snicm 	}
771a12989f1Snicm 
77258eb4b5dSnicm 	if (!OPTIONS_IS_STRING(o))
77358eb4b5dSnicm 		fatalx("option %s is not a string", name);
77484306383Snicm 	free(o->value.string);
77584306383Snicm 	o->value.string = value;
77601c0c428Snicm 	o->cached = 0;
77746de3b8aSnicm 	return (o);
778311827fbSnicm }
779311827fbSnicm 
78058900c88Snicm struct options_entry *
781311827fbSnicm options_set_number(struct options *oo, const char *name, long long value)
782311827fbSnicm {
78358900c88Snicm 	struct options_entry	*o;
784311827fbSnicm 
78558eb4b5dSnicm 	if (*name == '@')
78658eb4b5dSnicm 		fatalx("user option %s must be a string", name);
78782b5cde7Snicm 
78858eb4b5dSnicm 	o = options_get_only(oo, name);
78958eb4b5dSnicm 	if (o == NULL) {
79058eb4b5dSnicm 		o = options_default(oo, options_parent_table_entry(oo, name));
79158eb4b5dSnicm 		if (o == NULL)
79258eb4b5dSnicm 			return (NULL);
79358eb4b5dSnicm 	}
79458eb4b5dSnicm 
79558eb4b5dSnicm 	if (!OPTIONS_IS_NUMBER(o))
79658eb4b5dSnicm 		fatalx("option %s is not a number", name);
79784306383Snicm 	o->value.number = value;
79846de3b8aSnicm 	return (o);
799311827fbSnicm }
800311827fbSnicm 
8016e0f28f8Snicm int
802e8150bceSnicm options_scope_from_name(struct args *args, int window,
803e8150bceSnicm     const char *name, struct cmd_find_state *fs, struct options **oo,
804e8150bceSnicm     char **cause)
805e8150bceSnicm {
806e8150bceSnicm 	struct session				*s = fs->s;
807e8150bceSnicm 	struct winlink				*wl = fs->wl;
8086e0f28f8Snicm 	struct window_pane			*wp = fs->wp;
809e8150bceSnicm 	const char				*target = args_get(args, 't');
8106e0f28f8Snicm 	const struct options_table_entry	*oe;
811b35e7cffSnicm 	int					 scope = OPTIONS_TABLE_NONE;
812e8150bceSnicm 
813e8150bceSnicm 	if (*name == '@')
814e8150bceSnicm 		return (options_scope_from_flags(args, window, fs, oo, cause));
815e8150bceSnicm 
8166e0f28f8Snicm 	for (oe = options_table; oe->name != NULL; oe++) {
8176e0f28f8Snicm 		if (strcmp(oe->name, name) == 0)
8186e0f28f8Snicm 			break;
8196e0f28f8Snicm 	}
8206e0f28f8Snicm 	if (oe->name == NULL) {
821e8150bceSnicm 		xasprintf(cause, "unknown option: %s", name);
822e8150bceSnicm 		return (OPTIONS_TABLE_NONE);
823e8150bceSnicm 	}
824b35e7cffSnicm 	switch (oe->scope) {
8256e0f28f8Snicm 	case OPTIONS_TABLE_SERVER:
826e8150bceSnicm 		*oo = global_options;
827b35e7cffSnicm 		scope = OPTIONS_TABLE_SERVER;
8286e0f28f8Snicm 		break;
8296e0f28f8Snicm 	case OPTIONS_TABLE_SESSION:
830b35e7cffSnicm 		if (args_has(args, 'g')) {
831e8150bceSnicm 			*oo = global_s_options;
832b35e7cffSnicm 			scope = OPTIONS_TABLE_SESSION;
833b35e7cffSnicm 		} else if (s == NULL && target != NULL)
834e8150bceSnicm 			xasprintf(cause, "no such session: %s", target);
8356e0f28f8Snicm 		else if (s == NULL)
836e8150bceSnicm 			xasprintf(cause, "no current session");
837b35e7cffSnicm 		else {
838e8150bceSnicm 			*oo = s->options;
839b35e7cffSnicm 			scope = OPTIONS_TABLE_SESSION;
840b35e7cffSnicm 		}
8416e0f28f8Snicm 		break;
8426e0f28f8Snicm 	case OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE:
8436e0f28f8Snicm 		if (args_has(args, 'p')) {
8446e0f28f8Snicm 			if (wp == NULL && target != NULL)
8456e0f28f8Snicm 				xasprintf(cause, "no such pane: %s", target);
8466e0f28f8Snicm 			else if (wp == NULL)
8476e0f28f8Snicm 				xasprintf(cause, "no current pane");
848b35e7cffSnicm 			else {
8496e0f28f8Snicm 				*oo = wp->options;
850b35e7cffSnicm 				scope = OPTIONS_TABLE_PANE;
851b35e7cffSnicm 			}
8526e0f28f8Snicm 			break;
8536e0f28f8Snicm 		}
8546e0f28f8Snicm 		/* FALLTHROUGH */
8556e0f28f8Snicm 	case OPTIONS_TABLE_WINDOW:
856b35e7cffSnicm 		if (args_has(args, 'g')) {
857e8150bceSnicm 			*oo = global_w_options;
858b35e7cffSnicm 			scope = OPTIONS_TABLE_WINDOW;
859b35e7cffSnicm 		} else if (wl == NULL && target != NULL)
860e8150bceSnicm 			xasprintf(cause, "no such window: %s", target);
8616e0f28f8Snicm 		else if (wl == NULL)
862e8150bceSnicm 			xasprintf(cause, "no current window");
863b35e7cffSnicm 		else {
864e8150bceSnicm 			*oo = wl->window->options;
865b35e7cffSnicm 			scope = OPTIONS_TABLE_WINDOW;
866b35e7cffSnicm 		}
8676e0f28f8Snicm 		break;
868e8150bceSnicm 	}
869e8150bceSnicm 	return (scope);
870e8150bceSnicm }
871e8150bceSnicm 
8726e0f28f8Snicm int
87358eb4b5dSnicm options_scope_from_flags(struct args *args, int window,
87458eb4b5dSnicm     struct cmd_find_state *fs, struct options **oo, char **cause)
875a8c9f839Snicm {
87658eb4b5dSnicm 	struct session		*s = fs->s;
87758eb4b5dSnicm 	struct winlink		*wl = fs->wl;
8786e0f28f8Snicm 	struct window_pane	*wp = fs->wp;
87958eb4b5dSnicm 	const char		*target = args_get(args, 't');
880a8c9f839Snicm 
88158eb4b5dSnicm 	if (args_has(args, 's')) {
88258eb4b5dSnicm 		*oo = global_options;
88358eb4b5dSnicm 		return (OPTIONS_TABLE_SERVER);
88458eb4b5dSnicm 	}
88558eb4b5dSnicm 
8866e0f28f8Snicm 	if (args_has(args, 'p')) {
8876e0f28f8Snicm 		if (wp == NULL) {
8886e0f28f8Snicm 			if (target != NULL)
8896e0f28f8Snicm 				xasprintf(cause, "no such pane: %s", target);
8906e0f28f8Snicm 			else
8916e0f28f8Snicm 				xasprintf(cause, "no current pane");
8926e0f28f8Snicm 			return (OPTIONS_TABLE_NONE);
8936e0f28f8Snicm 		}
8946e0f28f8Snicm 		*oo = wp->options;
8956e0f28f8Snicm 		return (OPTIONS_TABLE_PANE);
8966e0f28f8Snicm 	} else if (window || args_has(args, 'w')) {
89758eb4b5dSnicm 		if (args_has(args, 'g')) {
89858eb4b5dSnicm 			*oo = global_w_options;
89958eb4b5dSnicm 			return (OPTIONS_TABLE_WINDOW);
90058eb4b5dSnicm 		}
90158eb4b5dSnicm 		if (wl == NULL) {
90258eb4b5dSnicm 			if (target != NULL)
90358eb4b5dSnicm 				xasprintf(cause, "no such window: %s", target);
90458eb4b5dSnicm 			else
90558eb4b5dSnicm 				xasprintf(cause, "no current window");
90658eb4b5dSnicm 			return (OPTIONS_TABLE_NONE);
90758eb4b5dSnicm 		}
90858eb4b5dSnicm 		*oo = wl->window->options;
90958eb4b5dSnicm 		return (OPTIONS_TABLE_WINDOW);
91058eb4b5dSnicm 	} else {
91158eb4b5dSnicm 		if (args_has(args, 'g')) {
91258eb4b5dSnicm 			*oo = global_s_options;
91358eb4b5dSnicm 			return (OPTIONS_TABLE_SESSION);
91458eb4b5dSnicm 		}
91558eb4b5dSnicm 		if (s == NULL) {
91658eb4b5dSnicm 			if (target != NULL)
91758eb4b5dSnicm 				xasprintf(cause, "no such session: %s", target);
91858eb4b5dSnicm 			else
91958eb4b5dSnicm 				xasprintf(cause, "no current session");
92058eb4b5dSnicm 			return (OPTIONS_TABLE_NONE);
92158eb4b5dSnicm 		}
92258eb4b5dSnicm 		*oo = s->options;
92358eb4b5dSnicm 		return (OPTIONS_TABLE_SESSION);
92458eb4b5dSnicm 	}
92558eb4b5dSnicm }
92601c0c428Snicm 
92701c0c428Snicm struct style *
92801c0c428Snicm options_string_to_style(struct options *oo, const char *name,
92901c0c428Snicm     struct format_tree *ft)
93001c0c428Snicm {
93101c0c428Snicm 	struct options_entry	*o;
93201c0c428Snicm 	const char		*s;
93301c0c428Snicm 	char			*expanded;
93401c0c428Snicm 
93501c0c428Snicm 	o = options_get(oo, name);
93601c0c428Snicm 	if (o == NULL || !OPTIONS_IS_STRING(o))
93701c0c428Snicm 		return (NULL);
93801c0c428Snicm 
93901c0c428Snicm 	if (o->cached)
94001c0c428Snicm 		return (&o->style);
94101c0c428Snicm 	s = o->value.string;
94201c0c428Snicm 	log_debug("%s: %s is '%s'", __func__, name, s);
94301c0c428Snicm 
94401c0c428Snicm 	style_set(&o->style, &grid_default_cell);
94501c0c428Snicm 	o->cached = (strstr(s, "#{") == NULL);
94601c0c428Snicm 
94701c0c428Snicm 	if (ft != NULL && !o->cached) {
94801c0c428Snicm 		expanded = format_expand(ft, s);
94901c0c428Snicm 		if (style_parse(&o->style, &grid_default_cell, expanded) != 0) {
95001c0c428Snicm 			free(expanded);
95101c0c428Snicm 			return (NULL);
95201c0c428Snicm 		}
95301c0c428Snicm 		free(expanded);
95401c0c428Snicm 	} else {
95501c0c428Snicm 		if (style_parse(&o->style, &grid_default_cell, s) != 0)
95601c0c428Snicm 			return (NULL);
95701c0c428Snicm 	}
95801c0c428Snicm 	return (&o->style);
95901c0c428Snicm }
96067c16a7cSnicm 
96167c16a7cSnicm static int
96267c16a7cSnicm options_from_string_check(const struct options_table_entry *oe,
96367c16a7cSnicm     const char *value, char **cause)
96467c16a7cSnicm {
96567c16a7cSnicm 	struct style	sy;
96667c16a7cSnicm 
96767c16a7cSnicm 	if (oe == NULL)
96867c16a7cSnicm 		return (0);
96967c16a7cSnicm 	if (strcmp(oe->name, "default-shell") == 0 && !checkshell(value)) {
97067c16a7cSnicm 		xasprintf(cause, "not a suitable shell: %s", value);
97167c16a7cSnicm 		return (-1);
97267c16a7cSnicm 	}
97367c16a7cSnicm 	if (oe->pattern != NULL && fnmatch(oe->pattern, value, 0) != 0) {
97467c16a7cSnicm 		xasprintf(cause, "value is invalid: %s", value);
97567c16a7cSnicm 		return (-1);
97667c16a7cSnicm 	}
97767c16a7cSnicm 	if ((oe->flags & OPTIONS_TABLE_IS_STYLE) &&
97867c16a7cSnicm 	    strstr(value, "#{") == NULL &&
97967c16a7cSnicm 	    style_parse(&sy, &grid_default_cell, value) != 0) {
98067c16a7cSnicm 		xasprintf(cause, "invalid style: %s", value);
98167c16a7cSnicm 		return (-1);
98267c16a7cSnicm 	}
98367c16a7cSnicm 	return (0);
98467c16a7cSnicm }
98567c16a7cSnicm 
98667c16a7cSnicm static int
98767c16a7cSnicm options_from_string_flag(struct options *oo, const char *name,
98867c16a7cSnicm     const char *value, char **cause)
98967c16a7cSnicm {
99067c16a7cSnicm 	int	flag;
99167c16a7cSnicm 
99267c16a7cSnicm 	if (value == NULL || *value == '\0')
99367c16a7cSnicm 		flag = !options_get_number(oo, name);
99467c16a7cSnicm 	else if (strcmp(value, "1") == 0 ||
99567c16a7cSnicm 	    strcasecmp(value, "on") == 0 ||
99667c16a7cSnicm 	    strcasecmp(value, "yes") == 0)
99767c16a7cSnicm 		flag = 1;
99867c16a7cSnicm 	else if (strcmp(value, "0") == 0 ||
99967c16a7cSnicm 	    strcasecmp(value, "off") == 0 ||
100067c16a7cSnicm 	    strcasecmp(value, "no") == 0)
100167c16a7cSnicm 		flag = 0;
100267c16a7cSnicm 	else {
100367c16a7cSnicm 		xasprintf(cause, "bad value: %s", value);
100467c16a7cSnicm 		return (-1);
100567c16a7cSnicm 	}
100667c16a7cSnicm 	options_set_number(oo, name, flag);
100767c16a7cSnicm 	return (0);
100867c16a7cSnicm }
100967c16a7cSnicm 
1010ccb627cdSnicm int
1011ccb627cdSnicm options_find_choice(const struct options_table_entry *oe, const char *value,
1012ccb627cdSnicm     char **cause)
101367c16a7cSnicm {
101467c16a7cSnicm 	const char	**cp;
1015ccb627cdSnicm 	int		  n = 0, choice = -1;
101667c16a7cSnicm 
101767c16a7cSnicm 	for (cp = oe->choices; *cp != NULL; cp++) {
101867c16a7cSnicm 		if (strcmp(*cp, value) == 0)
101967c16a7cSnicm 			choice = n;
102067c16a7cSnicm 		n++;
102167c16a7cSnicm 	}
102267c16a7cSnicm 	if (choice == -1) {
102367c16a7cSnicm 		xasprintf(cause, "unknown value: %s", value);
102467c16a7cSnicm 		return (-1);
102567c16a7cSnicm 	}
1026ccb627cdSnicm 	return (choice);
1027ccb627cdSnicm }
1028ccb627cdSnicm 
1029ccb627cdSnicm static int
1030ccb627cdSnicm options_from_string_choice(const struct options_table_entry *oe,
1031ccb627cdSnicm     struct options *oo, const char *name, const char *value, char **cause)
1032ccb627cdSnicm {
1033ccb627cdSnicm 	int	choice = -1;
1034ccb627cdSnicm 
1035ccb627cdSnicm 	if (value == NULL) {
1036ccb627cdSnicm 		choice = options_get_number(oo, name);
1037ccb627cdSnicm 		if (choice < 2)
1038ccb627cdSnicm 			choice = !choice;
1039ccb627cdSnicm 	} else {
1040ccb627cdSnicm 		choice = options_find_choice(oe, value, cause);
1041ccb627cdSnicm 		if (choice < 0)
1042ccb627cdSnicm 			return (-1);
104367c16a7cSnicm 	}
104467c16a7cSnicm 	options_set_number(oo, name, choice);
104567c16a7cSnicm 	return (0);
104667c16a7cSnicm }
104767c16a7cSnicm 
104867c16a7cSnicm int
104967c16a7cSnicm options_from_string(struct options *oo, const struct options_table_entry *oe,
105067c16a7cSnicm     const char *name, const char *value, int append, char **cause)
105167c16a7cSnicm {
105267c16a7cSnicm 	enum options_table_type	 type;
105367c16a7cSnicm 	long long		 number;
105467c16a7cSnicm 	const char		*errstr, *new;
105567c16a7cSnicm 	char			*old;
105667c16a7cSnicm 	key_code		 key;
105767c16a7cSnicm 
105867c16a7cSnicm 	if (oe != NULL) {
105967c16a7cSnicm 		if (value == NULL &&
106067c16a7cSnicm 		    oe->type != OPTIONS_TABLE_FLAG &&
106167c16a7cSnicm 		    oe->type != OPTIONS_TABLE_CHOICE) {
106267c16a7cSnicm 			xasprintf(cause, "empty value");
106367c16a7cSnicm 			return (-1);
106467c16a7cSnicm 		}
106567c16a7cSnicm 		type = oe->type;
106667c16a7cSnicm 	} else {
106767c16a7cSnicm 		if (*name != '@') {
106867c16a7cSnicm 			xasprintf(cause, "bad option name");
106967c16a7cSnicm 			return (-1);
107067c16a7cSnicm 		}
107167c16a7cSnicm 		type = OPTIONS_TABLE_STRING;
107267c16a7cSnicm 	}
107367c16a7cSnicm 
107467c16a7cSnicm 	switch (type) {
107567c16a7cSnicm 	case OPTIONS_TABLE_STRING:
107667c16a7cSnicm 		old = xstrdup(options_get_string(oo, name));
107767c16a7cSnicm 		options_set_string(oo, name, append, "%s", value);
107867c16a7cSnicm 
107967c16a7cSnicm 		new = options_get_string(oo, name);
108067c16a7cSnicm 		if (options_from_string_check(oe, new, cause) != 0) {
108167c16a7cSnicm 			options_set_string(oo, name, 0, "%s", old);
108267c16a7cSnicm 			free(old);
108367c16a7cSnicm 			return (-1);
108467c16a7cSnicm 		}
108567c16a7cSnicm 		free(old);
108667c16a7cSnicm 		return (0);
108767c16a7cSnicm 	case OPTIONS_TABLE_NUMBER:
108867c16a7cSnicm 		number = strtonum(value, oe->minimum, oe->maximum, &errstr);
108967c16a7cSnicm 		if (errstr != NULL) {
109067c16a7cSnicm 			xasprintf(cause, "value is %s: %s", errstr, value);
109167c16a7cSnicm 			return (-1);
109267c16a7cSnicm 		}
109367c16a7cSnicm 		options_set_number(oo, name, number);
109467c16a7cSnicm 		return (0);
109567c16a7cSnicm 	case OPTIONS_TABLE_KEY:
109667c16a7cSnicm 		key = key_string_lookup_string(value);
109767c16a7cSnicm 		if (key == KEYC_UNKNOWN) {
109867c16a7cSnicm 			xasprintf(cause, "bad key: %s", value);
109967c16a7cSnicm 			return (-1);
110067c16a7cSnicm 		}
110167c16a7cSnicm 		options_set_number(oo, name, key);
110267c16a7cSnicm 		return (0);
110367c16a7cSnicm 	case OPTIONS_TABLE_COLOUR:
110467c16a7cSnicm 		if ((number = colour_fromstring(value)) == -1) {
110567c16a7cSnicm 			xasprintf(cause, "bad colour: %s", value);
110667c16a7cSnicm 			return (-1);
110767c16a7cSnicm 		}
110867c16a7cSnicm 		options_set_number(oo, name, number);
110967c16a7cSnicm 		return (0);
111067c16a7cSnicm 	case OPTIONS_TABLE_FLAG:
111167c16a7cSnicm 		return (options_from_string_flag(oo, name, value, cause));
111267c16a7cSnicm 	case OPTIONS_TABLE_CHOICE:
111367c16a7cSnicm 		return (options_from_string_choice(oe, oo, name, value, cause));
111467c16a7cSnicm 	case OPTIONS_TABLE_COMMAND:
111567c16a7cSnicm 		break;
111667c16a7cSnicm 	}
111767c16a7cSnicm 	return (-1);
111867c16a7cSnicm }
111967c16a7cSnicm 
112067c16a7cSnicm void
112167c16a7cSnicm options_push_changes(const char *name)
112267c16a7cSnicm {
112367c16a7cSnicm 	struct client		*loop;
112467c16a7cSnicm 	struct session		*s;
112567c16a7cSnicm 	struct window		*w;
112667c16a7cSnicm 	struct window_pane	*wp;
112767c16a7cSnicm 
11282c25e4b4Snicm 	log_debug("%s: %s", __func__, name);
11292c25e4b4Snicm 
113067c16a7cSnicm 	if (strcmp(name, "automatic-rename") == 0) {
113167c16a7cSnicm 		RB_FOREACH(w, windows, &windows) {
113267c16a7cSnicm 			if (w->active == NULL)
113367c16a7cSnicm 				continue;
11341db1a6bbSnicm 			if (options_get_number(w->options, name))
113567c16a7cSnicm 				w->active->flags |= PANE_CHANGED;
113667c16a7cSnicm 		}
113767c16a7cSnicm 	}
11381db1a6bbSnicm 	if (strcmp(name, "cursor-colour") == 0) {
1139bd08eb64Snicm 		RB_FOREACH(wp, window_pane_tree, &all_window_panes)
1140bd08eb64Snicm 			window_pane_default_cursor(wp);
11411db1a6bbSnicm 	}
11426a238659Snicm 	if (strcmp(name, "cursor-style") == 0) {
1143bd08eb64Snicm 		RB_FOREACH(wp, window_pane_tree, &all_window_panes)
1144bd08eb64Snicm 			window_pane_default_cursor(wp);
11456a238659Snicm 	}
11462c25e4b4Snicm 	if (strcmp(name, "fill-character") == 0) {
11472c25e4b4Snicm 		RB_FOREACH(w, windows, &windows)
11482c25e4b4Snicm 			window_set_fill_character(w);
11492c25e4b4Snicm 	}
115067c16a7cSnicm 	if (strcmp(name, "key-table") == 0) {
115167c16a7cSnicm 		TAILQ_FOREACH(loop, &clients, entry)
115267c16a7cSnicm 			server_client_set_key_table(loop, NULL);
115367c16a7cSnicm 	}
115467c16a7cSnicm 	if (strcmp(name, "user-keys") == 0) {
115567c16a7cSnicm 		TAILQ_FOREACH(loop, &clients, entry) {
115667c16a7cSnicm 			if (loop->tty.flags & TTY_OPENED)
115767c16a7cSnicm 				tty_keys_build(&loop->tty);
115867c16a7cSnicm 		}
115967c16a7cSnicm 	}
116067c16a7cSnicm 	if (strcmp(name, "status") == 0 ||
116167c16a7cSnicm 	    strcmp(name, "status-interval") == 0)
116267c16a7cSnicm 		status_timer_start_all();
116367c16a7cSnicm 	if (strcmp(name, "monitor-silence") == 0)
116467c16a7cSnicm 		alerts_reset_all();
116567c16a7cSnicm 	if (strcmp(name, "window-style") == 0 ||
116667c16a7cSnicm 	    strcmp(name, "window-active-style") == 0) {
116767c16a7cSnicm 		RB_FOREACH(wp, window_pane_tree, &all_window_panes)
116867c16a7cSnicm 			wp->flags |= PANE_STYLECHANGED;
116967c16a7cSnicm 	}
117033a1e283Snicm 	if (strcmp(name, "pane-colours") == 0) {
117133a1e283Snicm 		RB_FOREACH(wp, window_pane_tree, &all_window_panes)
117233a1e283Snicm 			colour_palette_from_option(&wp->palette, wp->options);
117333a1e283Snicm 	}
1174d2117533Snicm 	if (strcmp(name, "pane-border-status") == 0 ||
1175d2117533Snicm 	    strcmp(name, "pane-scrollbars") == 0 ||
1176d2117533Snicm 	    strcmp(name, "pane-scrollbars-position") == 0) {
117767c16a7cSnicm 		RB_FOREACH(w, windows, &windows)
1178baddd6b2Snicm 			layout_fix_panes(w, NULL);
117967c16a7cSnicm 	}
1180dd0df669Snicm 	if (strcmp(name, "pane-scrollbars-style") == 0) {
1181dd0df669Snicm 		RB_FOREACH(wp, window_pane_tree, &all_window_panes) {
1182dd0df669Snicm 			style_set_scrollbar_style_from_option(
1183dd0df669Snicm 			    &wp->scrollbar_style, wp->options);
1184dd0df669Snicm 		}
1185dd0df669Snicm 		RB_FOREACH(w, windows, &windows)
1186dd0df669Snicm 			layout_fix_panes(w, NULL);
1187dd0df669Snicm 	}
1188*3d40d63aSnicm 	if (strcmp(name, "codepoint-widths") == 0)
1189*3d40d63aSnicm 		utf8_update_width_cache();
1190d5f1c1a5Snicm 	if (strcmp(name, "input-buffer-size") == 0)
1191d5f1c1a5Snicm 		input_set_buffer_size(options_get_number(global_options, name));
119267c16a7cSnicm 	RB_FOREACH(s, sessions, &sessions)
119367c16a7cSnicm 		status_update_cache(s);
119467c16a7cSnicm 
119567c16a7cSnicm 	recalculate_sizes();
119667c16a7cSnicm 	TAILQ_FOREACH(loop, &clients, entry) {
119767c16a7cSnicm 		if (loop->session != NULL)
119867c16a7cSnicm 			server_redraw_client(loop);
119967c16a7cSnicm 	}
120067c16a7cSnicm }
120194c0d63cSnicm 
120294c0d63cSnicm int
120394c0d63cSnicm options_remove_or_default(struct options_entry *o, int idx, char **cause)
120494c0d63cSnicm {
120594c0d63cSnicm 	struct options	*oo = o->owner;
120694c0d63cSnicm 
120794c0d63cSnicm 	if (idx == -1) {
120894c0d63cSnicm 		if (o->tableentry != NULL &&
120994c0d63cSnicm 		    (oo == global_options ||
121094c0d63cSnicm 		    oo == global_s_options ||
121194c0d63cSnicm 		    oo == global_w_options))
121294c0d63cSnicm 			options_default(oo, o->tableentry);
121394c0d63cSnicm 		else
121494c0d63cSnicm 			options_remove(o);
121594c0d63cSnicm 	} else if (options_array_set(o, idx, NULL, 0, cause) != 0)
121694c0d63cSnicm 		return (-1);
121794c0d63cSnicm 	return (0);
121894c0d63cSnicm }
1219