options.c revision 1.49
1/* $OpenBSD: options.c,v 1.49 2019/06/20 07:41:29 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 <stdarg.h>
23#include <stdlib.h>
24#include <string.h>
25
26#include "tmux.h"
27
28/*
29 * Option handling; each option has a name, type and value and is stored in
30 * a red-black tree.
31 */
32
33struct options_array_item {
34	u_int				 index;
35	union options_value		 value;
36	RB_ENTRY(options_array_item)	 entry;
37};
38static int
39options_array_cmp(struct options_array_item *a1, struct options_array_item *a2)
40{
41	if (a1->index < a2->index)
42		return (-1);
43	if (a1->index > a2->index)
44		return (1);
45	return (0);
46}
47RB_GENERATE_STATIC(options_array, options_array_item, entry, options_array_cmp);
48
49struct options_entry {
50	struct options				*owner;
51
52	const char				*name;
53	const struct options_table_entry	*tableentry;
54	union options_value			 value;
55
56	RB_ENTRY(options_entry)			 entry;
57};
58
59struct options {
60	RB_HEAD(options_tree, options_entry)	 tree;
61	struct options				*parent;
62};
63
64static struct options_entry	*options_add(struct options *, const char *);
65
66#define OPTIONS_IS_STRING(o)						\
67	((o)->tableentry == NULL ||					\
68	    (o)->tableentry->type == OPTIONS_TABLE_STRING)
69#define OPTIONS_IS_NUMBER(o) \
70	((o)->tableentry != NULL &&					\
71	    ((o)->tableentry->type == OPTIONS_TABLE_NUMBER ||		\
72	    (o)->tableentry->type == OPTIONS_TABLE_KEY ||		\
73	    (o)->tableentry->type == OPTIONS_TABLE_COLOUR ||		\
74	    (o)->tableentry->type == OPTIONS_TABLE_FLAG ||		\
75	    (o)->tableentry->type == OPTIONS_TABLE_CHOICE))
76#define OPTIONS_IS_STYLE(o) \
77	((o)->tableentry != NULL &&					\
78	    (o)->tableentry->type == OPTIONS_TABLE_STYLE)
79#define OPTIONS_IS_COMMAND(o) \
80	((o)->tableentry != NULL &&					\
81	    (o)->tableentry->type == OPTIONS_TABLE_COMMAND)
82
83#define OPTIONS_IS_ARRAY(o)						\
84	((o)->tableentry != NULL &&					\
85	    ((o)->tableentry->flags & OPTIONS_TABLE_IS_ARRAY))
86
87static int	options_cmp(struct options_entry *, struct options_entry *);
88RB_GENERATE_STATIC(options_tree, options_entry, entry, options_cmp);
89
90static int
91options_cmp(struct options_entry *lhs, struct options_entry *rhs)
92{
93	return (strcmp(lhs->name, rhs->name));
94}
95
96static const struct options_table_entry *
97options_parent_table_entry(struct options *oo, const char *s)
98{
99	struct options_entry	*o;
100
101	if (oo->parent == NULL)
102		fatalx("no parent options for %s", s);
103	o = options_get_only(oo->parent, s);
104	if (o == NULL)
105		fatalx("%s not in parent options", s);
106	return (o->tableentry);
107}
108
109static void
110options_value_free(struct options_entry *o, union options_value *ov)
111{
112	if (OPTIONS_IS_STRING(o))
113		free(ov->string);
114	if (OPTIONS_IS_COMMAND(o) && ov->cmdlist != NULL)
115		cmd_list_free(ov->cmdlist);
116}
117
118static char *
119options_value_tostring(struct options_entry *o, union options_value *ov,
120    int numeric)
121{
122	char	*s;
123
124	if (OPTIONS_IS_COMMAND(o))
125		return (cmd_list_print(ov->cmdlist, 0));
126	if (OPTIONS_IS_STYLE(o))
127		return (xstrdup(style_tostring(&ov->style)));
128	if (OPTIONS_IS_NUMBER(o)) {
129		switch (o->tableentry->type) {
130		case OPTIONS_TABLE_NUMBER:
131			xasprintf(&s, "%lld", ov->number);
132			break;
133		case OPTIONS_TABLE_KEY:
134			s = xstrdup(key_string_lookup_key(ov->number));
135			break;
136		case OPTIONS_TABLE_COLOUR:
137			s = xstrdup(colour_tostring(ov->number));
138			break;
139		case OPTIONS_TABLE_FLAG:
140			if (numeric)
141				xasprintf(&s, "%lld", ov->number);
142			else
143				s = xstrdup(ov->number ? "on" : "off");
144			break;
145		case OPTIONS_TABLE_CHOICE:
146			s = xstrdup(o->tableentry->choices[ov->number]);
147			break;
148		case OPTIONS_TABLE_STRING:
149		case OPTIONS_TABLE_STYLE:
150		case OPTIONS_TABLE_COMMAND:
151			fatalx("not a number option type");
152		}
153		return (s);
154	}
155	if (OPTIONS_IS_STRING(o))
156		return (xstrdup(ov->string));
157	return (xstrdup(""));
158}
159
160struct options *
161options_create(struct options *parent)
162{
163	struct options	*oo;
164
165	oo = xcalloc(1, sizeof *oo);
166	RB_INIT(&oo->tree);
167	oo->parent = parent;
168	return (oo);
169}
170
171void
172options_free(struct options *oo)
173{
174	struct options_entry	*o, *tmp;
175
176	RB_FOREACH_SAFE(o, options_tree, &oo->tree, tmp)
177		options_remove(o);
178	free(oo);
179}
180
181struct options_entry *
182options_first(struct options *oo)
183{
184	return (RB_MIN(options_tree, &oo->tree));
185}
186
187struct options_entry *
188options_next(struct options_entry *o)
189{
190	return (RB_NEXT(options_tree, &oo->tree, o));
191}
192
193struct options_entry *
194options_get_only(struct options *oo, const char *name)
195{
196	struct options_entry	o;
197
198	o.name = name;
199	return (RB_FIND(options_tree, &oo->tree, &o));
200}
201
202struct options_entry *
203options_get(struct options *oo, const char *name)
204{
205	struct options_entry	*o;
206
207	o = options_get_only(oo, name);
208	while (o == NULL) {
209		oo = oo->parent;
210		if (oo == NULL)
211			break;
212		o = options_get_only(oo, name);
213	}
214	return (o);
215}
216
217struct options_entry *
218options_empty(struct options *oo, const struct options_table_entry *oe)
219{
220	struct options_entry	*o;
221
222	o = options_add(oo, oe->name);
223	o->tableentry = oe;
224
225	if (oe->flags & OPTIONS_TABLE_IS_ARRAY)
226		RB_INIT(&o->value.array);
227
228	return (o);
229}
230
231struct options_entry *
232options_default(struct options *oo, const struct options_table_entry *oe)
233{
234	struct options_entry	*o;
235	union options_value	*ov;
236	u_int			 i;
237
238	o = options_empty(oo, oe);
239	ov = &o->value;
240
241	if (oe->flags & OPTIONS_TABLE_IS_ARRAY) {
242		if (oe->default_arr == NULL) {
243			options_array_assign(o, oe->default_str, NULL);
244			return (o);
245		}
246		for (i = 0; oe->default_arr[i] != NULL; i++)
247			options_array_set(o, i, oe->default_arr[i], 0, NULL);
248		return (o);
249	}
250
251	switch (oe->type) {
252	case OPTIONS_TABLE_STRING:
253		ov->string = xstrdup(oe->default_str);
254		break;
255	case OPTIONS_TABLE_STYLE:
256		style_set(&ov->style, &grid_default_cell);
257		style_parse(&ov->style, &grid_default_cell, oe->default_str);
258		break;
259	default:
260		ov->number = oe->default_num;
261		break;
262	}
263	return (o);
264}
265
266static struct options_entry *
267options_add(struct options *oo, const char *name)
268{
269	struct options_entry	*o;
270
271	o = options_get_only(oo, name);
272	if (o != NULL)
273		options_remove(o);
274
275	o = xcalloc(1, sizeof *o);
276	o->owner = oo;
277	o->name = xstrdup(name);
278
279	RB_INSERT(options_tree, &oo->tree, o);
280	return (o);
281}
282
283void
284options_remove(struct options_entry *o)
285{
286	struct options	*oo = o->owner;
287
288	if (OPTIONS_IS_ARRAY(o))
289		options_array_clear(o);
290	else
291		options_value_free(o, &o->value);
292	RB_REMOVE(options_tree, &oo->tree, o);
293	free(o);
294}
295
296const char *
297options_name(struct options_entry *o)
298{
299	return (o->name);
300}
301
302const struct options_table_entry *
303options_table_entry(struct options_entry *o)
304{
305	return (o->tableentry);
306}
307
308static struct options_array_item *
309options_array_item(struct options_entry *o, u_int idx)
310{
311	struct options_array_item	a;
312
313	a.index = idx;
314	return (RB_FIND(options_array, &o->value.array, &a));
315}
316
317static void
318options_array_free(struct options_entry *o, struct options_array_item *a)
319{
320	options_value_free(o, &a->value);
321	RB_REMOVE(options_array, &o->value.array, a);
322	free(a);
323}
324
325void
326options_array_clear(struct options_entry *o)
327{
328	struct options_array_item	*a, *a1;
329
330	if (!OPTIONS_IS_ARRAY(o))
331		return;
332
333	RB_FOREACH_SAFE(a, options_array, &o->value.array, a1)
334	    options_array_free(o, a);
335}
336
337union options_value *
338options_array_get(struct options_entry *o, u_int idx)
339{
340	struct options_array_item	*a;
341
342	if (!OPTIONS_IS_ARRAY(o))
343		return (NULL);
344	a = options_array_item(o, idx);
345	if (a == NULL)
346		return (NULL);
347	return (&a->value);
348}
349
350int
351options_array_set(struct options_entry *o, u_int idx, const char *value,
352    int append, char **cause)
353{
354	struct options_array_item	*a;
355	char				*new;
356	struct cmd_parse_result		*pr;
357
358	if (!OPTIONS_IS_ARRAY(o)) {
359		if (cause != NULL)
360			*cause = xstrdup("not an array");
361		return (-1);
362	}
363
364	if (OPTIONS_IS_COMMAND(o) && value != NULL) {
365		pr = cmd_parse_from_string(value, NULL);
366		switch (pr->status) {
367		case CMD_PARSE_EMPTY:
368			if (cause != NULL)
369				*cause = xstrdup("empty command");
370			return (-1);
371		case CMD_PARSE_ERROR:
372			if (cause != NULL)
373				*cause = pr->error;
374			else
375				free(pr->error);
376			return (-1);
377		case CMD_PARSE_SUCCESS:
378			break;
379		}
380	}
381
382	a = options_array_item(o, idx);
383	if (value == NULL) {
384		if (a != NULL)
385			options_array_free(o, a);
386		return (0);
387	}
388
389	if (OPTIONS_IS_STRING(o)) {
390		if (a != NULL && append)
391			xasprintf(&new, "%s%s", a->value.string, value);
392		else
393			new = xstrdup(value);
394	}
395
396	if (a == NULL) {
397		a = xcalloc(1, sizeof *a);
398		a->index = idx;
399		RB_INSERT(options_array, &o->value.array, a);
400	} else
401		options_value_free(o, &a->value);
402
403	if (OPTIONS_IS_STRING(o))
404		a->value.string = new;
405	else if (OPTIONS_IS_COMMAND(o))
406		a->value.cmdlist = pr->cmdlist;
407	return (0);
408}
409
410int
411options_array_assign(struct options_entry *o, const char *s, char **cause)
412{
413	const char	*separator;
414	char		*copy, *next, *string;
415	u_int		 i;
416
417	separator = o->tableentry->separator;
418	if (separator == NULL)
419		separator = " ,";
420	if (*separator == '\0') {
421		if (*s == '\0')
422			return (0);
423		for (i = 0; i < UINT_MAX; i++) {
424			if (options_array_item(o, i) == NULL)
425				break;
426		}
427		return (options_array_set(o, i, s, 0, cause));
428	}
429
430	if (*s == '\0')
431		return (0);
432	copy = string = xstrdup(s);
433	while ((next = strsep(&string, separator)) != NULL) {
434		if (*next == '\0')
435			continue;
436		for (i = 0; i < UINT_MAX; i++) {
437			if (options_array_item(o, i) == NULL)
438				break;
439		}
440		if (i == UINT_MAX)
441			break;
442		if (options_array_set(o, i, next, 0, cause) != 0) {
443			free(copy);
444			return (-1);
445		}
446	}
447	free(copy);
448	return (0);
449}
450
451struct options_array_item *
452options_array_first(struct options_entry *o)
453{
454	if (!OPTIONS_IS_ARRAY(o))
455		return (NULL);
456	return (RB_MIN(options_array, &o->value.array));
457}
458
459struct options_array_item *
460options_array_next(struct options_array_item *a)
461{
462	return (RB_NEXT(options_array, &o->value.array, a));
463}
464
465u_int
466options_array_item_index(struct options_array_item *a)
467{
468	return (a->index);
469}
470
471union options_value *
472options_array_item_value(struct options_array_item *a)
473{
474	return (&a->value);
475}
476
477int
478options_isarray(struct options_entry *o)
479{
480	return (OPTIONS_IS_ARRAY(o));
481}
482
483int
484options_isstring(struct options_entry *o)
485{
486	return (OPTIONS_IS_STRING(o));
487}
488
489char *
490options_tostring(struct options_entry *o, int idx, int numeric)
491{
492	struct options_array_item	*a;
493
494	if (OPTIONS_IS_ARRAY(o)) {
495		if (idx == -1)
496			return (xstrdup(""));
497		a = options_array_item(o, idx);
498		if (a == NULL)
499			return (xstrdup(""));
500		return (options_value_tostring(o, &a->value, numeric));
501	}
502	return (options_value_tostring(o, &o->value, numeric));
503}
504
505char *
506options_parse(const char *name, int *idx)
507{
508	char	*copy, *cp, *end;
509
510	if (*name == '\0')
511		return (NULL);
512	copy = xstrdup(name);
513	if ((cp = strchr(copy, '[')) == NULL) {
514		*idx = -1;
515		return (copy);
516	}
517	end = strchr(cp + 1, ']');
518	if (end == NULL || end[1] != '\0' || !isdigit((u_char)end[-1])) {
519		free(copy);
520		return (NULL);
521	}
522	if (sscanf(cp, "[%d]", idx) != 1 || *idx < 0) {
523		free(copy);
524		return (NULL);
525	}
526	*cp = '\0';
527	return (copy);
528}
529
530struct options_entry *
531options_parse_get(struct options *oo, const char *s, int *idx, int only)
532{
533	struct options_entry	*o;
534	char			*name;
535
536	name = options_parse(s, idx);
537	if (name == NULL)
538		return (NULL);
539	if (only)
540		o = options_get_only(oo, name);
541	else
542		o = options_get(oo, name);
543	free(name);
544	return (o);
545}
546
547char *
548options_match(const char *s, int *idx, int* ambiguous)
549{
550	const struct options_table_entry	*oe, *found;
551	char					*name;
552	size_t					 namelen;
553
554	name = options_parse(s, idx);
555	if (name == NULL)
556		return (NULL);
557	namelen = strlen(name);
558
559	if (*name == '@') {
560		*ambiguous = 0;
561		return (name);
562	}
563
564	found = NULL;
565	for (oe = options_table; oe->name != NULL; oe++) {
566		if (strcmp(oe->name, name) == 0) {
567			found = oe;
568			break;
569		}
570		if (strncmp(oe->name, name, namelen) == 0) {
571			if (found != NULL) {
572				*ambiguous = 1;
573				free(name);
574				return (NULL);
575			}
576			found = oe;
577		}
578	}
579	free(name);
580	if (found == NULL) {
581		*ambiguous = 0;
582		return (NULL);
583	}
584	return (xstrdup(found->name));
585}
586
587struct options_entry *
588options_match_get(struct options *oo, const char *s, int *idx, int only,
589    int* ambiguous)
590{
591	char			*name;
592	struct options_entry	*o;
593
594	name = options_match(s, idx, ambiguous);
595	if (name == NULL)
596		return (NULL);
597	*ambiguous = 0;
598	if (only)
599		o = options_get_only(oo, name);
600	else
601		o = options_get(oo, name);
602	free(name);
603	return (o);
604}
605
606const char *
607options_get_string(struct options *oo, const char *name)
608{
609	struct options_entry	*o;
610
611	o = options_get(oo, name);
612	if (o == NULL)
613		fatalx("missing option %s", name);
614	if (!OPTIONS_IS_STRING(o))
615		fatalx("option %s is not a string", name);
616	return (o->value.string);
617}
618
619long long
620options_get_number(struct options *oo, const char *name)
621{
622	struct options_entry	*o;
623
624	o = options_get(oo, name);
625	if (o == NULL)
626		fatalx("missing option %s", name);
627	if (!OPTIONS_IS_NUMBER(o))
628	    fatalx("option %s is not a number", name);
629	return (o->value.number);
630}
631
632struct style *
633options_get_style(struct options *oo, const char *name)
634{
635	struct options_entry	*o;
636
637	o = options_get(oo, name);
638	if (o == NULL)
639		fatalx("missing option %s", name);
640	if (!OPTIONS_IS_STYLE(o))
641		fatalx("option %s is not a style", name);
642	return (&o->value.style);
643}
644
645struct options_entry *
646options_set_string(struct options *oo, const char *name, int append,
647    const char *fmt, ...)
648{
649	struct options_entry	*o;
650	va_list			 ap;
651	char			*s, *value;
652
653	va_start(ap, fmt);
654	xvasprintf(&s, fmt, ap);
655	va_end(ap);
656
657	o = options_get_only(oo, name);
658	if (o != NULL && append && OPTIONS_IS_STRING(o)) {
659		xasprintf(&value, "%s%s", o->value.string, s);
660		free(s);
661	} else
662		value = s;
663	if (o == NULL && *name == '@')
664		o = options_add(oo, name);
665	else if (o == NULL) {
666		o = options_default(oo, options_parent_table_entry(oo, name));
667		if (o == NULL)
668			return (NULL);
669	}
670
671	if (!OPTIONS_IS_STRING(o))
672		fatalx("option %s is not a string", name);
673	free(o->value.string);
674	o->value.string = value;
675	return (o);
676}
677
678struct options_entry *
679options_set_number(struct options *oo, const char *name, long long value)
680{
681	struct options_entry	*o;
682
683	if (*name == '@')
684		fatalx("user option %s must be a string", name);
685
686	o = options_get_only(oo, name);
687	if (o == NULL) {
688		o = options_default(oo, options_parent_table_entry(oo, name));
689		if (o == NULL)
690			return (NULL);
691	}
692
693	if (!OPTIONS_IS_NUMBER(o))
694		fatalx("option %s is not a number", name);
695	o->value.number = value;
696	return (o);
697}
698
699struct options_entry *
700options_set_style(struct options *oo, const char *name, int append,
701    const char *value)
702{
703	struct options_entry	*o;
704	struct style		 sy;
705
706	if (*name == '@')
707		fatalx("user option %s must be a string", name);
708
709	o = options_get_only(oo, name);
710	if (o != NULL && append && OPTIONS_IS_STYLE(o))
711		style_copy(&sy, &o->value.style);
712	else
713		style_set(&sy, &grid_default_cell);
714	if (style_parse(&sy, &grid_default_cell, value) == -1)
715		return (NULL);
716	if (o == NULL) {
717		o = options_default(oo, options_parent_table_entry(oo, name));
718		if (o == NULL)
719			return (NULL);
720	}
721
722	if (!OPTIONS_IS_STYLE(o))
723		fatalx("option %s is not a style", name);
724	style_copy(&o->value.style, &sy);
725	return (o);
726}
727
728enum options_table_scope
729options_scope_from_name(struct args *args, int window,
730    const char *name, struct cmd_find_state *fs, struct options **oo,
731    char **cause)
732{
733	struct session			*s = fs->s;
734	struct winlink			*wl = fs->wl;
735	const char			*target = args_get(args, 't');
736	enum options_table_scope	 scope;
737
738	if (*name == '@')
739		return (options_scope_from_flags(args, window, fs, oo, cause));
740
741	if (options_get_only(global_options, name) != NULL)
742		scope = OPTIONS_TABLE_SERVER;
743	else if (options_get_only(global_s_options, name) != NULL)
744		scope = OPTIONS_TABLE_SESSION;
745	else if (options_get_only(global_w_options, name) != NULL)
746		scope = OPTIONS_TABLE_WINDOW;
747	else {
748		xasprintf(cause, "unknown option: %s", name);
749		return (OPTIONS_TABLE_NONE);
750	}
751
752	if (scope == OPTIONS_TABLE_SERVER)
753		*oo = global_options;
754	else if (scope == OPTIONS_TABLE_SESSION) {
755		if (args_has(args, 'g'))
756			*oo = global_s_options;
757		else if (s == NULL) {
758			if (target != NULL)
759				xasprintf(cause, "no such session: %s", target);
760			else
761				xasprintf(cause, "no current session");
762		} else
763			*oo = s->options;
764	} else if (scope == OPTIONS_TABLE_WINDOW) {
765		if (args_has(args, 'g'))
766			*oo = global_w_options;
767		else if (wl == NULL) {
768			if (target != NULL)
769				xasprintf(cause, "no such window: %s", target);
770			else
771				xasprintf(cause, "no current window");
772		} else
773			*oo = wl->window->options;
774	}
775	return (scope);
776}
777
778enum options_table_scope
779options_scope_from_flags(struct args *args, int window,
780    struct cmd_find_state *fs, struct options **oo, char **cause)
781{
782	struct session	*s = fs->s;
783	struct winlink	*wl = fs->wl;
784	const char	*target = args_get(args, 't');
785
786	if (args_has(args, 's')) {
787		*oo = global_options;
788		return (OPTIONS_TABLE_SERVER);
789	}
790
791	if (window || args_has(args, 'w')) {
792		if (args_has(args, 'g')) {
793			*oo = global_w_options;
794			return (OPTIONS_TABLE_WINDOW);
795		}
796		if (wl == NULL) {
797			if (target != NULL)
798				xasprintf(cause, "no such window: %s", target);
799			else
800				xasprintf(cause, "no current window");
801			return (OPTIONS_TABLE_NONE);
802		}
803		*oo = wl->window->options;
804		return (OPTIONS_TABLE_WINDOW);
805	} else {
806		if (args_has(args, 'g')) {
807			*oo = global_s_options;
808			return (OPTIONS_TABLE_SESSION);
809		}
810		if (s == NULL) {
811			if (target != NULL)
812				xasprintf(cause, "no such session: %s", target);
813			else
814				xasprintf(cause, "no current session");
815			return (OPTIONS_TABLE_NONE);
816		}
817		*oo = s->options;
818		return (OPTIONS_TABLE_SESSION);
819	}
820}
821