options.c revision 1.36
1/* $OpenBSD: options.c,v 1.36 2017/08/09 13:44:36 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, int numeric)
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			if (numeric)
359				xsnprintf(s, sizeof s, "%lld", o->number);
360			else
361				tmp = (o->number ? "on" : "off");
362			break;
363		case OPTIONS_TABLE_CHOICE:
364			tmp = o->tableentry->choices[o->number];
365			break;
366		case OPTIONS_TABLE_STRING:
367		case OPTIONS_TABLE_STYLE:
368		case OPTIONS_TABLE_ARRAY:
369			break;
370		}
371		if (tmp != NULL)
372			xsnprintf(s, sizeof s, "%s", tmp);
373		return (s);
374	}
375	if (OPTIONS_IS_STRING(o))
376		return (o->string);
377	return (NULL);
378}
379
380char *
381options_parse(const char *name, int *idx)
382{
383	char	*copy, *cp, *end;
384
385	if (*name == '\0')
386		return (NULL);
387	copy = xstrdup(name);
388	if ((cp = strchr(copy, '[')) == NULL) {
389		*idx = -1;
390		return (copy);
391	}
392	end = strchr(cp + 1, ']');
393	if (end == NULL || end[1] != '\0' || !isdigit((u_char)end[-1])) {
394		free(copy);
395		return (NULL);
396	}
397	if (sscanf(cp, "[%d]", idx) != 1 || *idx < 0) {
398		free(copy);
399		return (NULL);
400	}
401	*cp = '\0';
402	return (copy);
403}
404
405struct options_entry *
406options_parse_get(struct options *oo, const char *s, int *idx, int only)
407{
408	struct options_entry	*o;
409	char			*name;
410
411	name = options_parse(s, idx);
412	if (name == NULL)
413		return (NULL);
414	if (only)
415		o = options_get_only(oo, name);
416	else
417		o = options_get(oo, name);
418	free(name);
419	return (o);
420}
421
422char *
423options_match(const char *s, int *idx, int* ambiguous)
424{
425	const struct options_table_entry	*oe, *found;
426	char					*name;
427	size_t					 namelen;
428
429	name = options_parse(s, idx);
430	if (name == NULL)
431		return (NULL);
432	namelen = strlen(name);
433
434	if (*name == '@') {
435		*ambiguous = 0;
436		return (name);
437	}
438
439	found = NULL;
440	for (oe = options_table; oe->name != NULL; oe++) {
441		if (strcmp(oe->name, name) == 0) {
442			found = oe;
443			break;
444		}
445		if (strncmp(oe->name, name, namelen) == 0) {
446			if (found != NULL) {
447				*ambiguous = 1;
448				free(name);
449				return (NULL);
450			}
451			found = oe;
452		}
453	}
454	free(name);
455	if (found == NULL) {
456		*ambiguous = 0;
457		return (NULL);
458	}
459	return (xstrdup(found->name));
460}
461
462struct options_entry *
463options_match_get(struct options *oo, const char *s, int *idx, int only,
464    int* ambiguous)
465{
466	char			*name;
467	struct options_entry	*o;
468
469	name = options_match(s, idx, ambiguous);
470	if (name == NULL)
471		return (NULL);
472	*ambiguous = 0;
473	if (only)
474		o = options_get_only(oo, name);
475	else
476		o = options_get(oo, name);
477	free(name);
478	return (o);
479}
480
481const char *
482options_get_string(struct options *oo, const char *name)
483{
484	struct options_entry	*o;
485
486	o = options_get(oo, name);
487	if (o == NULL)
488		fatalx("missing option %s", name);
489	if (!OPTIONS_IS_STRING(o))
490		fatalx("option %s is not a string", name);
491	return (o->string);
492}
493
494long long
495options_get_number(struct options *oo, const char *name)
496{
497	struct options_entry	*o;
498
499	o = options_get(oo, name);
500	if (o == NULL)
501		fatalx("missing option %s", name);
502	if (!OPTIONS_IS_NUMBER(o))
503	    fatalx("option %s is not a number", name);
504	return (o->number);
505}
506
507const struct grid_cell *
508options_get_style(struct options *oo, const char *name)
509{
510	struct options_entry	*o;
511
512	o = options_get(oo, name);
513	if (o == NULL)
514		fatalx("missing option %s", name);
515	if (!OPTIONS_IS_STYLE(o))
516		fatalx("option %s is not a style", name);
517	return (&o->style);
518}
519
520struct options_entry *
521options_set_string(struct options *oo, const char *name, int append,
522    const char *fmt, ...)
523{
524	struct options_entry	*o;
525	va_list			 ap;
526	char			*s, *value;
527
528	va_start(ap, fmt);
529	xvasprintf(&s, fmt, ap);
530	va_end(ap);
531
532	o = options_get_only(oo, name);
533	if (o != NULL && append && OPTIONS_IS_STRING(o)) {
534		xasprintf(&value, "%s%s", o->string, s);
535		free(s);
536	} else
537		value = s;
538	if (o == NULL && *name == '@')
539		o = options_add(oo, name);
540	else if (o == NULL) {
541		o = options_default(oo, options_parent_table_entry(oo, name));
542		if (o == NULL)
543			return (NULL);
544	}
545
546	if (!OPTIONS_IS_STRING(o))
547		fatalx("option %s is not a string", name);
548	free(o->string);
549	o->string = value;
550	return (o);
551}
552
553struct options_entry *
554options_set_number(struct options *oo, const char *name, long long value)
555{
556	struct options_entry	*o;
557
558	if (*name == '@')
559		fatalx("user option %s must be a string", name);
560
561	o = options_get_only(oo, name);
562	if (o == NULL) {
563		o = options_default(oo, options_parent_table_entry(oo, name));
564		if (o == NULL)
565			return (NULL);
566	}
567
568	if (!OPTIONS_IS_NUMBER(o))
569		fatalx("option %s is not a number", name);
570	o->number = value;
571	return (o);
572}
573
574struct options_entry *
575options_set_style(struct options *oo, const char *name, int append,
576    const char *value)
577{
578	struct options_entry	*o;
579	struct grid_cell	 gc;
580
581	if (*name == '@')
582		fatalx("user option %s must be a string", name);
583
584	o = options_get_only(oo, name);
585	if (o != NULL && append && OPTIONS_IS_STYLE(o))
586		memcpy(&gc, &o->style, sizeof gc);
587	else
588		memcpy(&gc, &grid_default_cell, sizeof gc);
589	if (style_parse(&grid_default_cell, &gc, value) == -1)
590		return (NULL);
591	if (o == NULL) {
592		o = options_default(oo, options_parent_table_entry(oo, name));
593		if (o == NULL)
594			return (NULL);
595	}
596
597	if (!OPTIONS_IS_STYLE(o))
598		fatalx("option %s is not a style", name);
599	memcpy(&o->style, &gc, sizeof o->style);
600	return (o);
601}
602
603enum options_table_scope
604options_scope_from_flags(struct args *args, int window,
605    struct cmd_find_state *fs, struct options **oo, char **cause)
606{
607	struct session	*s = fs->s;
608	struct winlink	*wl = fs->wl;
609	const char	*target= args_get(args, 't');
610
611	if (args_has(args, 's')) {
612		*oo = global_options;
613		return (OPTIONS_TABLE_SERVER);
614	}
615
616	if (window || args_has(args, 'w')) {
617		if (args_has(args, 'g')) {
618			*oo = global_w_options;
619			return (OPTIONS_TABLE_WINDOW);
620		}
621		if (wl == NULL) {
622			if (target != NULL)
623				xasprintf(cause, "no such window: %s", target);
624			else
625				xasprintf(cause, "no current window");
626			return (OPTIONS_TABLE_NONE);
627		}
628		*oo = wl->window->options;
629		return (OPTIONS_TABLE_WINDOW);
630	} else {
631		if (args_has(args, 'g')) {
632			*oo = global_s_options;
633			return (OPTIONS_TABLE_SESSION);
634		}
635		if (s == NULL) {
636			if (target != NULL)
637				xasprintf(cause, "no such session: %s", target);
638			else
639				xasprintf(cause, "no current session");
640			return (OPTIONS_TABLE_NONE);
641		}
642		*oo = s->options;
643		return (OPTIONS_TABLE_SESSION);
644	}
645}
646
647void
648options_style_update_new(struct options *oo, struct options_entry *o)
649{
650	const char		*newname = o->tableentry->style;
651	struct options_entry	*new;
652
653	if (newname == NULL)
654		return;
655	new = options_get_only(oo, newname);
656	if (new == NULL)
657		new = options_set_style(oo, newname, 0, "default");
658
659	if (strstr(o->name, "-bg") != NULL)
660		new->style.bg = o->number;
661	else if (strstr(o->name, "-fg") != NULL)
662		new->style.fg = o->number;
663	else if (strstr(o->name, "-attr") != NULL)
664		new->style.attr = o->number;
665}
666
667void
668options_style_update_old(struct options *oo, struct options_entry *o)
669{
670	char	newname[128];
671	int	size;
672
673	size = strrchr(o->name, '-') - o->name;
674
675	xsnprintf(newname, sizeof newname, "%.*s-bg", size, o->name);
676	if (options_get(oo, newname) != NULL)
677		options_set_number(oo, newname, o->style.bg);
678
679	xsnprintf(newname, sizeof newname, "%.*s-fg", size, o->name);
680	if (options_get(oo, newname) != NULL)
681		options_set_number(oo, newname, o->style.fg);
682
683	xsnprintf(newname, sizeof newname, "%.*s-attr", size, o->name);
684	if (options_get(oo, newname) != NULL)
685		options_set_number(oo, newname, o->style.attr);
686}
687