options.c revision 1.31
1/* $OpenBSD: options.c,v 1.31 2017/01/24 19:11:46 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
174	o = options_empty(oo, oe);
175	if (oe->type == OPTIONS_TABLE_ARRAY)
176		options_array_assign(o, oe->default_str);
177	else if (oe->type == OPTIONS_TABLE_STRING)
178		o->string = xstrdup(oe->default_str);
179	else if (oe->type == OPTIONS_TABLE_STYLE) {
180		memcpy(&o->style, &grid_default_cell, sizeof o->style);
181		style_parse(&grid_default_cell, &o->style, oe->default_str);
182	} else
183		o->number = oe->default_num;
184	return (o);
185}
186
187static struct options_entry *
188options_add(struct options *oo, const char *name)
189{
190	struct options_entry	*o;
191
192	o = options_get_only(oo, name);
193	if (o != NULL)
194		options_remove(o);
195
196	o = xcalloc(1, sizeof *o);
197	o->owner = oo;
198	o->name = xstrdup(name);
199
200	RB_INSERT(options_tree, &oo->tree, o);
201	return (o);
202}
203
204void
205options_remove(struct options_entry *o)
206{
207	struct options	*oo = o->owner;
208	u_int		 i;
209
210	if (OPTIONS_IS_STRING(o))
211		free((void *)o->string);
212	else if (OPTIONS_IS_ARRAY(o)) {
213		for (i = 0; i < o->arraysize; i++)
214			free((void *)o->array[i]);
215		free(o->array);
216	}
217
218	RB_REMOVE(options_tree, &oo->tree, o);
219	free(o);
220}
221
222const char *
223options_name(struct options_entry *o)
224{
225	return (o->name);
226}
227
228const struct options_table_entry *
229options_table_entry(struct options_entry *o)
230{
231	return (o->tableentry);
232}
233
234void
235options_array_clear(struct options_entry *o)
236{
237	if (OPTIONS_IS_ARRAY(o))
238		o->arraysize = 0;
239}
240
241const char *
242options_array_get(struct options_entry *o, u_int idx)
243{
244	if (!OPTIONS_IS_ARRAY(o))
245		return (NULL);
246	if (idx >= o->arraysize)
247		return (NULL);
248	return (o->array[idx]);
249}
250
251int
252options_array_set(struct options_entry *o, u_int idx, const char *value,
253    int append)
254{
255	char	*new;
256	u_int	 i;
257
258	if (!OPTIONS_IS_ARRAY(o))
259		return (-1);
260
261	if (idx >= OPTIONS_ARRAY_LIMIT)
262		return (-1);
263	if (idx >= o->arraysize) {
264		o->array = xreallocarray(o->array, idx + 1, sizeof *o->array);
265		for (i = o->arraysize; i < idx + 1; i++)
266			o->array[i] = NULL;
267		o->arraysize = idx + 1;
268	}
269
270	new = NULL;
271	if (value != NULL) {
272		if (o->array[idx] != NULL && append)
273			xasprintf(&new, "%s%s", o->array[idx], value);
274		else
275			new = xstrdup(value);
276	}
277
278	free((void *)o->array[idx]);
279	o->array[idx] = new;
280	return (0);
281}
282
283int
284options_array_size(struct options_entry *o, u_int *size)
285{
286	if (!OPTIONS_IS_ARRAY(o))
287		return (-1);
288	if (size != NULL)
289		*size = o->arraysize;
290	return (0);
291}
292
293void
294options_array_assign(struct options_entry *o, const char *s)
295{
296	const char	*separator;
297	char		*copy, *next, *string;
298	u_int		 i;
299
300	separator = o->tableentry->separator;
301	if (separator == NULL)
302		separator = " ,";
303
304	copy = string = xstrdup(s);
305	while ((next = strsep(&string, separator)) != NULL) {
306		if (*next == '\0')
307			continue;
308		for (i = 0; i < OPTIONS_ARRAY_LIMIT; i++) {
309			if (i >= o->arraysize || o->array[i] == NULL)
310				break;
311		}
312		if (i == OPTIONS_ARRAY_LIMIT)
313			break;
314		options_array_set(o, i, next, 0);
315	}
316	free(copy);
317}
318
319int
320options_isstring(struct options_entry *o)
321{
322	if (o->tableentry == NULL)
323		return (1);
324	return (OPTIONS_IS_STRING(o) || OPTIONS_IS_ARRAY(o));
325}
326
327const char *
328options_tostring(struct options_entry *o, int idx)
329{
330	static char	 s[1024];
331	const char	*tmp;
332
333	if (OPTIONS_IS_ARRAY(o)) {
334		if (idx == -1)
335			return (NULL);
336		if ((u_int)idx >= o->arraysize || o->array[idx] == NULL)
337			return ("");
338		return (o->array[idx]);
339	}
340	if (OPTIONS_IS_STYLE(o))
341		return (style_tostring(&o->style));
342	if (OPTIONS_IS_NUMBER(o)) {
343		tmp = NULL;
344		switch (o->tableentry->type) {
345		case OPTIONS_TABLE_NUMBER:
346			xsnprintf(s, sizeof s, "%lld", o->number);
347			break;
348		case OPTIONS_TABLE_KEY:
349			tmp = key_string_lookup_key(o->number);
350			break;
351		case OPTIONS_TABLE_COLOUR:
352			tmp = colour_tostring(o->number);
353			break;
354		case OPTIONS_TABLE_ATTRIBUTES:
355			tmp = attributes_tostring(o->number);
356			break;
357		case OPTIONS_TABLE_FLAG:
358			tmp = (o->number ? "on" : "off");
359			break;
360		case OPTIONS_TABLE_CHOICE:
361			tmp = o->tableentry->choices[o->number];
362			break;
363		case OPTIONS_TABLE_STRING:
364		case OPTIONS_TABLE_STYLE:
365		case OPTIONS_TABLE_ARRAY:
366			break;
367		}
368		if (tmp != NULL)
369			xsnprintf(s, sizeof s, "%s", tmp);
370		return (s);
371	}
372	if (OPTIONS_IS_STRING(o))
373		return (o->string);
374	return (NULL);
375}
376
377char *
378options_parse(const char *name, int *idx)
379{
380	char	*copy, *cp, *end;
381
382	if (*name == '\0')
383		return (NULL);
384	copy = xstrdup(name);
385	if ((cp = strchr(copy, '[')) == NULL) {
386		*idx = -1;
387		return (copy);
388	}
389	end = strchr(cp + 1, ']');
390	if (end == NULL || end[1] != '\0' || !isdigit((u_char)end[-1])) {
391		free(copy);
392		return (NULL);
393	}
394	if (sscanf(cp, "[%d]", idx) != 1 || *idx < 0) {
395		free(copy);
396		return (NULL);
397	}
398	*cp = '\0';
399	return (copy);
400}
401
402struct options_entry *
403options_parse_get(struct options *oo, const char *s, int *idx, int only)
404{
405	struct options_entry	*o;
406	char			*name;
407
408	name = options_parse(s, idx);
409	if (name == NULL)
410		return (NULL);
411	if (only)
412		o = options_get_only(oo, name);
413	else
414		o = options_get(oo, name);
415	free(name);
416	return (o);
417}
418
419char *
420options_match(const char *s, int *idx, int* ambiguous)
421{
422	const struct options_table_entry	*oe, *found;
423	char					*name;
424	size_t					 namelen;
425
426	name = options_parse(s, idx);
427	namelen = strlen(name);
428
429	if (*name == '@') {
430		*ambiguous = 0;
431		return (xstrdup(name));
432	}
433
434	found = NULL;
435	for (oe = options_table; oe->name != NULL; oe++) {
436		if (strcmp(oe->name, name) == 0) {
437			found = oe;
438			break;
439		}
440		if (strncmp(oe->name, name, namelen) == 0) {
441			if (found != NULL) {
442				*ambiguous = 1;
443				free(name);
444				return (NULL);
445			}
446			found = oe;
447		}
448	}
449	free(name);
450	if (found == NULL) {
451		*ambiguous = 0;
452		return (NULL);
453	}
454	return (xstrdup(found->name));
455}
456
457struct options_entry *
458options_match_get(struct options *oo, const char *s, int *idx, int only,
459    int* ambiguous)
460{
461	char			*name;
462	struct options_entry	*o;
463
464	name = options_match(s, idx, ambiguous);
465	if (name == NULL)
466		return (NULL);
467	*ambiguous = 0;
468	if (only)
469		o = options_get_only(oo, name);
470	else
471		o = options_get(oo, name);
472	free(name);
473	return (o);
474}
475
476
477const char *
478options_get_string(struct options *oo, const char *name)
479{
480	struct options_entry	*o;
481
482	o = options_get(oo, name);
483	if (o == NULL)
484		fatalx("missing option %s", name);
485	if (!OPTIONS_IS_STRING(o))
486		fatalx("option %s is not a string", name);
487	return (o->string);
488}
489
490long long
491options_get_number(struct options *oo, const char *name)
492{
493	struct options_entry	*o;
494
495	o = options_get(oo, name);
496	if (o == NULL)
497		fatalx("missing option %s", name);
498	if (!OPTIONS_IS_NUMBER(o))
499	    fatalx("option %s is not a number", name);
500	return (o->number);
501}
502
503const struct grid_cell *
504options_get_style(struct options *oo, const char *name)
505{
506	struct options_entry	*o;
507
508	o = options_get(oo, name);
509	if (o == NULL)
510		fatalx("missing option %s", name);
511	if (!OPTIONS_IS_STYLE(o))
512		fatalx("option %s is not a style", name);
513	return (&o->style);
514}
515
516struct options_entry *
517options_set_string(struct options *oo, const char *name, int append,
518    const char *fmt, ...)
519{
520	struct options_entry	*o;
521	va_list			 ap;
522	char			*s, *value;
523
524	va_start(ap, fmt);
525	xvasprintf(&s, fmt, ap);
526	va_end(ap);
527
528	o = options_get_only(oo, name);
529	if (o != NULL && append && OPTIONS_IS_STRING(o)) {
530		xasprintf(&value, "%s%s", o->string, s);
531		free(s);
532	} else
533		value = s;
534	if (o == NULL && *name == '@')
535		o = options_add(oo, name);
536	else if (o == NULL) {
537		o = options_default(oo, options_parent_table_entry(oo, name));
538		if (o == NULL)
539			return (NULL);
540	}
541
542	if (!OPTIONS_IS_STRING(o))
543		fatalx("option %s is not a string", name);
544	free(o->string);
545	o->string = value;
546	return (o);
547}
548
549struct options_entry *
550options_set_number(struct options *oo, const char *name, long long value)
551{
552	struct options_entry	*o;
553
554	if (*name == '@')
555		fatalx("user option %s must be a string", name);
556
557	o = options_get_only(oo, name);
558	if (o == NULL) {
559		o = options_default(oo, options_parent_table_entry(oo, name));
560		if (o == NULL)
561			return (NULL);
562	}
563
564	if (!OPTIONS_IS_NUMBER(o))
565		fatalx("option %s is not a number", name);
566	o->number = value;
567	return (o);
568}
569
570struct options_entry *
571options_set_style(struct options *oo, const char *name, int append,
572    const char *value)
573{
574	struct options_entry	*o;
575	struct grid_cell	 gc;
576
577	if (*name == '@')
578		fatalx("user option %s must be a string", name);
579
580	o = options_get_only(oo, name);
581	if (o != NULL && append && OPTIONS_IS_STYLE(o))
582		memcpy(&gc, &o->style, sizeof gc);
583	else
584		memcpy(&gc, &grid_default_cell, sizeof gc);
585	if (style_parse(&grid_default_cell, &gc, value) == -1)
586		return (NULL);
587	if (o == NULL) {
588		o = options_default(oo, options_parent_table_entry(oo, name));
589		if (o == NULL)
590			return (NULL);
591	}
592
593	if (!OPTIONS_IS_STYLE(o))
594		fatalx("option %s is not a style", name);
595	memcpy(&o->style, &gc, sizeof o->style);
596	return (o);
597}
598
599enum options_table_scope
600options_scope_from_flags(struct args *args, int window,
601    struct cmd_find_state *fs, struct options **oo, char **cause)
602{
603	struct session	*s = fs->s;
604	struct winlink	*wl = fs->wl;
605	const char	*target= args_get(args, 't');
606
607	if (args_has(args, 's')) {
608		*oo = global_options;
609		return (OPTIONS_TABLE_SERVER);
610	}
611
612	if (window || args_has(args, 'w')) {
613		if (args_has(args, 'g')) {
614			*oo = global_w_options;
615			return (OPTIONS_TABLE_WINDOW);
616		}
617		if (wl == NULL) {
618			if (target != NULL)
619				xasprintf(cause, "no such window: %s", target);
620			else
621				xasprintf(cause, "no current window");
622			return (OPTIONS_TABLE_NONE);
623		}
624		*oo = wl->window->options;
625		return (OPTIONS_TABLE_WINDOW);
626	} else {
627		if (args_has(args, 'g')) {
628			*oo = global_s_options;
629			return (OPTIONS_TABLE_SESSION);
630		}
631		if (s == NULL) {
632			if (target != NULL)
633				xasprintf(cause, "no such session: %s", target);
634			else
635				xasprintf(cause, "no current session");
636			return (OPTIONS_TABLE_NONE);
637		}
638		*oo = s->options;
639		return (OPTIONS_TABLE_SESSION);
640	}
641}
642
643void
644options_style_update_new(struct options *oo, struct options_entry *o)
645{
646	const char		*newname = o->tableentry->style;
647	struct options_entry	*new;
648
649	if (newname == NULL)
650		return;
651	new = options_get_only(oo, newname);
652	if (new == NULL)
653		new = options_set_style(oo, newname, 0, "default");
654
655	if (strstr(o->name, "-bg") != NULL)
656		new->style.bg = o->number;
657	else if (strstr(o->name, "-fg") != NULL)
658		new->style.fg = o->number;
659	else if (strstr(o->name, "-attr") != NULL)
660		new->style.attr = o->number;
661}
662
663void
664options_style_update_old(struct options *oo, struct options_entry *o)
665{
666	char	newname[128];
667	int	size;
668
669	size = strrchr(o->name, '-') - o->name;
670
671	xsnprintf(newname, sizeof newname, "%.*s-bg", size, o->name);
672	if (options_get(oo, newname) != NULL)
673		options_set_number(oo, newname, o->style.bg);
674
675	xsnprintf(newname, sizeof newname, "%.*s-fg", size, o->name);
676	if (options_get(oo, newname) != NULL)
677		options_set_number(oo, newname, o->style.fg);
678
679	xsnprintf(newname, sizeof newname, "%.*s-attr", size, o->name);
680	if (options_get(oo, newname) != NULL)
681		options_set_number(oo, newname, o->style.attr);
682}
683