options.c revision 1.30
1/* $OpenBSD: options.c,v 1.30 2017/01/18 08:43:21 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_entry {
34	struct options				 *owner;
35
36	const char				 *name;
37	const struct options_table_entry	 *tableentry;
38
39	union {
40		char				 *string;
41		long long			  number;
42		struct grid_cell		  style;
43		struct {
44			const char		**array;
45			u_int			  arraysize;
46		};
47	};
48
49	RB_ENTRY(options_entry)			  entry;
50};
51
52struct options {
53	RB_HEAD(options_tree, options_entry)	 tree;
54	struct options				*parent;
55};
56
57static struct options_entry	*options_add(struct options *, const char *);
58
59#define OPTIONS_ARRAY_LIMIT 1000
60
61#define OPTIONS_IS_STRING(o)						\
62	((o)->tableentry == NULL ||					\
63	    (o)->tableentry->type == OPTIONS_TABLE_STRING)
64#define OPTIONS_IS_NUMBER(o) \
65	((o)->tableentry != NULL &&					\
66	    ((o)->tableentry->type == OPTIONS_TABLE_NUMBER ||		\
67	    (o)->tableentry->type == OPTIONS_TABLE_KEY ||		\
68	    (o)->tableentry->type == OPTIONS_TABLE_COLOUR ||		\
69	    (o)->tableentry->type == OPTIONS_TABLE_ATTRIBUTES ||	\
70	    (o)->tableentry->type == OPTIONS_TABLE_FLAG ||		\
71	    (o)->tableentry->type == OPTIONS_TABLE_CHOICE))
72#define OPTIONS_IS_STYLE(o) \
73	((o)->tableentry != NULL &&					\
74	    (o)->tableentry->type == OPTIONS_TABLE_STYLE)
75#define OPTIONS_IS_ARRAY(o) \
76	((o)->tableentry != NULL &&					\
77	    (o)->tableentry->type == OPTIONS_TABLE_ARRAY)
78
79static int	options_cmp(struct options_entry *, struct options_entry *);
80RB_GENERATE_STATIC(options_tree, options_entry, entry, options_cmp);
81
82static int
83options_cmp(struct options_entry *lhs, struct options_entry *rhs)
84{
85	return (strcmp(lhs->name, rhs->name));
86}
87
88static const struct options_table_entry *
89options_parent_table_entry(struct options *oo, const char *s)
90{
91	struct options_entry	*o;
92
93	if (oo->parent == NULL)
94		fatalx("no parent options for %s", s);
95	o = options_get_only(oo->parent, s);
96	if (o == NULL)
97		fatalx("%s not in parent options", s);
98	return (o->tableentry);
99}
100
101struct options *
102options_create(struct options *parent)
103{
104	struct options	*oo;
105
106	oo = xcalloc(1, sizeof *oo);
107	RB_INIT(&oo->tree);
108	oo->parent = parent;
109	return (oo);
110}
111
112void
113options_free(struct options *oo)
114{
115	struct options_entry	*o, *tmp;
116
117	RB_FOREACH_SAFE (o, options_tree, &oo->tree, tmp)
118		options_remove(o);
119	free(oo);
120}
121
122struct options_entry *
123options_first(struct options *oo)
124{
125	return (RB_MIN(options_tree, &oo->tree));
126}
127
128struct options_entry *
129options_next(struct options_entry *o)
130{
131	return (RB_NEXT(options_tree, &oo->tree, o));
132}
133
134struct options_entry *
135options_get_only(struct options *oo, const char *name)
136{
137	struct options_entry	o;
138
139	o.name = name;
140	return (RB_FIND(options_tree, &oo->tree, &o));
141}
142
143struct options_entry *
144options_get(struct options *oo, const char *name)
145{
146	struct options_entry	*o;
147
148	o = options_get_only(oo, name);
149	while (o == NULL) {
150		oo = oo->parent;
151		if (oo == NULL)
152			break;
153		o = options_get_only(oo, name);
154	}
155	return (o);
156}
157
158struct options_entry *
159options_empty(struct options *oo, const struct options_table_entry *oe)
160{
161	struct options_entry	*o;
162
163	o = options_add(oo, oe->name);
164	o->tableentry = oe;
165
166	return (o);
167}
168
169struct options_entry *
170options_default(struct options *oo, const struct options_table_entry *oe)
171{
172	struct options_entry	*o;
173	char			*cp, *copy, *next;
174	u_int			 idx = 0;
175
176	o = options_empty(oo, oe);
177
178	if (oe->type == OPTIONS_TABLE_ARRAY) {
179		copy = cp = xstrdup(oe->default_str);
180		while ((next = strsep(&cp, ",")) != NULL) {
181			options_array_set(o, idx, next);
182			idx++;
183		}
184		free(copy);
185		return (o);
186	}
187
188	if (oe->type == OPTIONS_TABLE_STRING)
189		o->string = xstrdup(oe->default_str);
190	else if (oe->type == OPTIONS_TABLE_STYLE) {
191		memcpy(&o->style, &grid_default_cell, sizeof o->style);
192		style_parse(&grid_default_cell, &o->style, oe->default_str);
193	} else
194		o->number = oe->default_num;
195	return (o);
196}
197
198static struct options_entry *
199options_add(struct options *oo, const char *name)
200{
201	struct options_entry	*o;
202
203	o = options_get_only(oo, name);
204	if (o != NULL)
205		options_remove(o);
206
207	o = xcalloc(1, sizeof *o);
208	o->owner = oo;
209	o->name = xstrdup(name);
210
211	RB_INSERT(options_tree, &oo->tree, o);
212	return (o);
213}
214
215void
216options_remove(struct options_entry *o)
217{
218	struct options	*oo = o->owner;
219	u_int		 i;
220
221	if (OPTIONS_IS_STRING(o))
222		free((void *)o->string);
223	else if (OPTIONS_IS_ARRAY(o)) {
224		for (i = 0; i < o->arraysize; i++)
225			free((void *)o->array[i]);
226		free(o->array);
227	}
228
229	RB_REMOVE(options_tree, &oo->tree, o);
230	free(o);
231}
232
233const char *
234options_name(struct options_entry *o)
235{
236	return (o->name);
237}
238
239const struct options_table_entry *
240options_table_entry(struct options_entry *o)
241{
242	return (o->tableentry);
243}
244
245const char *
246options_array_get(struct options_entry *o, u_int idx)
247{
248	if (!OPTIONS_IS_ARRAY(o))
249		return (NULL);
250	if (idx >= o->arraysize)
251		return (NULL);
252	return (o->array[idx]);
253}
254
255int
256options_array_set(struct options_entry *o, u_int idx, const char *value)
257{
258	u_int	i;
259
260	if (!OPTIONS_IS_ARRAY(o))
261		return (-1);
262
263	if (idx >= OPTIONS_ARRAY_LIMIT)
264		return (-1);
265	if (idx >= o->arraysize) {
266		o->array = xreallocarray(o->array, idx + 1, sizeof *o->array);
267		for (i = o->arraysize; i < idx + 1; i++)
268			o->array[i] = NULL;
269		o->arraysize = idx + 1;
270	}
271	if (o->array[idx] != NULL)
272		free((void *)o->array[idx]);
273	if (value != NULL)
274		o->array[idx] = xstrdup(value);
275	else
276		o->array[idx] = NULL;
277	return (0);
278}
279
280int
281options_array_size(struct options_entry *o, u_int *size)
282{
283	if (!OPTIONS_IS_ARRAY(o))
284		return (-1);
285	if (size != NULL)
286		*size = o->arraysize;
287	return (0);
288}
289
290int
291options_isstring(struct options_entry *o)
292{
293	if (o->tableentry == NULL)
294		return (1);
295	return (OPTIONS_IS_STRING(o) || OPTIONS_IS_ARRAY(o));
296}
297
298const char *
299options_tostring(struct options_entry *o, int idx)
300{
301	static char	 s[1024];
302	const char	*tmp;
303
304	if (OPTIONS_IS_ARRAY(o)) {
305		if (idx == -1)
306			return (NULL);
307		if ((u_int)idx >= o->arraysize || o->array[idx] == NULL)
308			return ("");
309		return (o->array[idx]);
310	}
311	if (OPTIONS_IS_STYLE(o))
312		return (style_tostring(&o->style));
313	if (OPTIONS_IS_NUMBER(o)) {
314		tmp = NULL;
315		switch (o->tableentry->type) {
316		case OPTIONS_TABLE_NUMBER:
317			xsnprintf(s, sizeof s, "%lld", o->number);
318			break;
319		case OPTIONS_TABLE_KEY:
320			tmp = key_string_lookup_key(o->number);
321			break;
322		case OPTIONS_TABLE_COLOUR:
323			tmp = colour_tostring(o->number);
324			break;
325		case OPTIONS_TABLE_ATTRIBUTES:
326			tmp = attributes_tostring(o->number);
327			break;
328		case OPTIONS_TABLE_FLAG:
329			tmp = (o->number ? "on" : "off");
330			break;
331		case OPTIONS_TABLE_CHOICE:
332			tmp = o->tableentry->choices[o->number];
333			break;
334		case OPTIONS_TABLE_STRING:
335		case OPTIONS_TABLE_STYLE:
336		case OPTIONS_TABLE_ARRAY:
337			break;
338		}
339		if (tmp != NULL)
340			xsnprintf(s, sizeof s, "%s", tmp);
341		return (s);
342	}
343	if (OPTIONS_IS_STRING(o))
344		return (o->string);
345	return (NULL);
346}
347
348char *
349options_parse(const char *name, int *idx)
350{
351	char	*copy, *cp, *end;
352
353	if (*name == '\0')
354		return (NULL);
355	copy = xstrdup(name);
356	if ((cp = strchr(copy, '[')) == NULL) {
357		*idx = -1;
358		return (copy);
359	}
360	end = strchr(cp + 1, ']');
361	if (end == NULL || end[1] != '\0' || !isdigit((u_char)end[-1])) {
362		free(copy);
363		return (NULL);
364	}
365	if (sscanf(cp, "[%d]", idx) != 1 || *idx < 0) {
366		free(copy);
367		return (NULL);
368	}
369	*cp = '\0';
370	return (copy);
371}
372
373struct options_entry *
374options_parse_get(struct options *oo, const char *s, int *idx, int only)
375{
376	struct options_entry	*o;
377	char			*name;
378
379	name = options_parse(s, idx);
380	if (name == NULL)
381		return (NULL);
382	if (only)
383		o = options_get_only(oo, name);
384	else
385		o = options_get(oo, name);
386	free(name);
387	if (o != NULL) {
388		if (OPTIONS_IS_ARRAY(o) && *idx == -1)
389			return (NULL);
390		if (!OPTIONS_IS_ARRAY(o) && *idx != -1)
391			return (NULL);
392	}
393	return (o);
394}
395
396char *
397options_match(const char *s, int *idx, int* ambiguous)
398{
399	const struct options_table_entry	*oe, *found;
400	char					*name;
401	size_t					 namelen;
402
403	name = options_parse(s, idx);
404	namelen = strlen(name);
405
406	if (*name == '@') {
407		*ambiguous = 0;
408		return (xstrdup(name));
409	}
410
411	found = NULL;
412	for (oe = options_table; oe->name != NULL; oe++) {
413		if (strcmp(oe->name, name) == 0) {
414			found = oe;
415			break;
416		}
417		if (strncmp(oe->name, name, namelen) == 0) {
418			if (found != NULL) {
419				*ambiguous = 1;
420				free(name);
421				return (NULL);
422			}
423			found = oe;
424		}
425	}
426	free(name);
427	if (found == NULL) {
428		*ambiguous = 0;
429		return (NULL);
430	}
431	return (xstrdup(found->name));
432}
433
434struct options_entry *
435options_match_get(struct options *oo, const char *s, int *idx, int only,
436    int* ambiguous)
437{
438	char			*name;
439	struct options_entry	*o;
440
441	name = options_match(s, idx, ambiguous);
442	if (name == NULL)
443		return (NULL);
444	*ambiguous = 0;
445	if (only)
446		o = options_get_only(oo, name);
447	else
448		o = options_get(oo, name);
449	free(name);
450	if (o != NULL) {
451		if (OPTIONS_IS_ARRAY(o) && *idx == -1)
452			return (NULL);
453		if (!OPTIONS_IS_ARRAY(o) && *idx != -1)
454			return (NULL);
455	}
456	return (o);
457}
458
459
460const char *
461options_get_string(struct options *oo, const char *name)
462{
463	struct options_entry	*o;
464
465	o = options_get(oo, name);
466	if (o == NULL)
467		fatalx("missing option %s", name);
468	if (!OPTIONS_IS_STRING(o))
469		fatalx("option %s is not a string", name);
470	return (o->string);
471}
472
473long long
474options_get_number(struct options *oo, const char *name)
475{
476	struct options_entry	*o;
477
478	o = options_get(oo, name);
479	if (o == NULL)
480		fatalx("missing option %s", name);
481	if (!OPTIONS_IS_NUMBER(o))
482	    fatalx("option %s is not a number", name);
483	return (o->number);
484}
485
486const struct grid_cell *
487options_get_style(struct options *oo, const char *name)
488{
489	struct options_entry	*o;
490
491	o = options_get(oo, name);
492	if (o == NULL)
493		fatalx("missing option %s", name);
494	if (!OPTIONS_IS_STYLE(o))
495		fatalx("option %s is not a style", name);
496	return (&o->style);
497}
498
499struct options_entry *
500options_set_string(struct options *oo, const char *name, int append,
501    const char *fmt, ...)
502{
503	struct options_entry	*o;
504	va_list			 ap;
505	char			*s, *value;
506
507	va_start(ap, fmt);
508	xvasprintf(&s, fmt, ap);
509	va_end(ap);
510
511	o = options_get_only(oo, name);
512	if (o != NULL && append && OPTIONS_IS_STRING(o)) {
513		xasprintf(&value, "%s%s", o->string, s);
514		free(s);
515	} else
516		value = s;
517	if (o == NULL && *name == '@')
518		o = options_add(oo, name);
519	else if (o == NULL) {
520		o = options_default(oo, options_parent_table_entry(oo, name));
521		if (o == NULL)
522			return (NULL);
523	}
524
525	if (!OPTIONS_IS_STRING(o))
526		fatalx("option %s is not a string", name);
527	free(o->string);
528	o->string = value;
529	return (o);
530}
531
532struct options_entry *
533options_set_number(struct options *oo, const char *name, long long value)
534{
535	struct options_entry	*o;
536
537	if (*name == '@')
538		fatalx("user option %s must be a string", name);
539
540	o = options_get_only(oo, name);
541	if (o == NULL) {
542		o = options_default(oo, options_parent_table_entry(oo, name));
543		if (o == NULL)
544			return (NULL);
545	}
546
547	if (!OPTIONS_IS_NUMBER(o))
548		fatalx("option %s is not a number", name);
549	o->number = value;
550	return (o);
551}
552
553struct options_entry *
554options_set_style(struct options *oo, const char *name, int append,
555    const char *value)
556{
557	struct options_entry	*o;
558	struct grid_cell	 gc;
559
560	if (*name == '@')
561		fatalx("user option %s must be a string", name);
562
563	o = options_get_only(oo, name);
564	if (o != NULL && append && OPTIONS_IS_STYLE(o))
565		memcpy(&gc, &o->style, sizeof gc);
566	else
567		memcpy(&gc, &grid_default_cell, sizeof gc);
568	if (style_parse(&grid_default_cell, &gc, value) == -1)
569		return (NULL);
570	if (o == NULL) {
571		o = options_default(oo, options_parent_table_entry(oo, name));
572		if (o == NULL)
573			return (NULL);
574	}
575
576	if (!OPTIONS_IS_STYLE(o))
577		fatalx("option %s is not a style", name);
578	memcpy(&o->style, &gc, sizeof o->style);
579	return (o);
580}
581
582enum options_table_scope
583options_scope_from_flags(struct args *args, int window,
584    struct cmd_find_state *fs, struct options **oo, char **cause)
585{
586	struct session	*s = fs->s;
587	struct winlink	*wl = fs->wl;
588	const char	*target= args_get(args, 't');
589
590	if (args_has(args, 's')) {
591		*oo = global_options;
592		return (OPTIONS_TABLE_SERVER);
593	}
594
595	if (window || args_has(args, 'w')) {
596		if (args_has(args, 'g')) {
597			*oo = global_w_options;
598			return (OPTIONS_TABLE_WINDOW);
599		}
600		if (wl == NULL) {
601			if (target != NULL)
602				xasprintf(cause, "no such window: %s", target);
603			else
604				xasprintf(cause, "no current window");
605			return (OPTIONS_TABLE_NONE);
606		}
607		*oo = wl->window->options;
608		return (OPTIONS_TABLE_WINDOW);
609	} else {
610		if (args_has(args, 'g')) {
611			*oo = global_s_options;
612			return (OPTIONS_TABLE_SESSION);
613		}
614		if (s == NULL) {
615			if (target != NULL)
616				xasprintf(cause, "no such session: %s", target);
617			else
618				xasprintf(cause, "no current session");
619			return (OPTIONS_TABLE_NONE);
620		}
621		*oo = s->options;
622		return (OPTIONS_TABLE_SESSION);
623	}
624}
625
626void
627options_style_update_new(struct options *oo, struct options_entry *o)
628{
629	const char		*newname = o->tableentry->style;
630	struct options_entry	*new;
631
632	if (newname == NULL)
633		return;
634	new = options_get_only(oo, newname);
635	if (new == NULL)
636		new = options_set_style(oo, newname, 0, "default");
637
638	if (strstr(o->name, "-bg") != NULL)
639		new->style.bg = o->number;
640	else if (strstr(o->name, "-fg") != NULL)
641		new->style.fg = o->number;
642	else if (strstr(o->name, "-attr") != NULL)
643		new->style.attr = o->number;
644}
645
646void
647options_style_update_old(struct options *oo, struct options_entry *o)
648{
649	char	newname[128];
650	int	size;
651
652	size = strrchr(o->name, '-') - o->name;
653
654	xsnprintf(newname, sizeof newname, "%.*s-bg", size, o->name);
655	if (options_get(oo, newname) != NULL)
656		options_set_number(oo, newname, o->style.bg);
657
658	xsnprintf(newname, sizeof newname, "%.*s-fg", size, o->name);
659	if (options_get(oo, newname) != NULL)
660		options_set_number(oo, newname, o->style.fg);
661
662	xsnprintf(newname, sizeof newname, "%.*s-attr", size, o->name);
663	if (options_get(oo, newname) != NULL)
664		options_set_number(oo, newname, o->style.attr);
665}
666