xref: /openbsd/usr.bin/tmux/options.c (revision 6f40fd34)
1 /* $OpenBSD: options.c,v 1.35 2017/05/31 17:56:48 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 
33 struct 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 
52 struct options {
53 	RB_HEAD(options_tree, options_entry)	 tree;
54 	struct options				*parent;
55 };
56 
57 static 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 
79 static int	options_cmp(struct options_entry *, struct options_entry *);
80 RB_GENERATE_STATIC(options_tree, options_entry, entry, options_cmp);
81 
82 static int
83 options_cmp(struct options_entry *lhs, struct options_entry *rhs)
84 {
85 	return (strcmp(lhs->name, rhs->name));
86 }
87 
88 static const struct options_table_entry *
89 options_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 
101 struct options *
102 options_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 
112 void
113 options_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 
122 struct options_entry *
123 options_first(struct options *oo)
124 {
125 	return (RB_MIN(options_tree, &oo->tree));
126 }
127 
128 struct options_entry *
129 options_next(struct options_entry *o)
130 {
131 	return (RB_NEXT(options_tree, &oo->tree, o));
132 }
133 
134 struct options_entry *
135 options_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 
143 struct options_entry *
144 options_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 
158 struct options_entry *
159 options_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 
169 struct options_entry *
170 options_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 
187 static struct options_entry *
188 options_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 
204 void
205 options_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 
222 const char *
223 options_name(struct options_entry *o)
224 {
225 	return (o->name);
226 }
227 
228 const struct options_table_entry *
229 options_table_entry(struct options_entry *o)
230 {
231 	return (o->tableentry);
232 }
233 
234 void
235 options_array_clear(struct options_entry *o)
236 {
237 	if (OPTIONS_IS_ARRAY(o))
238 		o->arraysize = 0;
239 }
240 
241 const char *
242 options_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 
251 int
252 options_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 
283 int
284 options_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 
293 void
294 options_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 
319 int
320 options_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 
327 const char *
328 options_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 
380 char *
381 options_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 
405 struct options_entry *
406 options_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 
422 char *
423 options_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 
462 struct options_entry *
463 options_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 
481 
482 const char *
483 options_get_string(struct options *oo, const char *name)
484 {
485 	struct options_entry	*o;
486 
487 	o = options_get(oo, name);
488 	if (o == NULL)
489 		fatalx("missing option %s", name);
490 	if (!OPTIONS_IS_STRING(o))
491 		fatalx("option %s is not a string", name);
492 	return (o->string);
493 }
494 
495 long long
496 options_get_number(struct options *oo, const char *name)
497 {
498 	struct options_entry	*o;
499 
500 	o = options_get(oo, name);
501 	if (o == NULL)
502 		fatalx("missing option %s", name);
503 	if (!OPTIONS_IS_NUMBER(o))
504 	    fatalx("option %s is not a number", name);
505 	return (o->number);
506 }
507 
508 const struct grid_cell *
509 options_get_style(struct options *oo, const char *name)
510 {
511 	struct options_entry	*o;
512 
513 	o = options_get(oo, name);
514 	if (o == NULL)
515 		fatalx("missing option %s", name);
516 	if (!OPTIONS_IS_STYLE(o))
517 		fatalx("option %s is not a style", name);
518 	return (&o->style);
519 }
520 
521 struct options_entry *
522 options_set_string(struct options *oo, const char *name, int append,
523     const char *fmt, ...)
524 {
525 	struct options_entry	*o;
526 	va_list			 ap;
527 	char			*s, *value;
528 
529 	va_start(ap, fmt);
530 	xvasprintf(&s, fmt, ap);
531 	va_end(ap);
532 
533 	o = options_get_only(oo, name);
534 	if (o != NULL && append && OPTIONS_IS_STRING(o)) {
535 		xasprintf(&value, "%s%s", o->string, s);
536 		free(s);
537 	} else
538 		value = s;
539 	if (o == NULL && *name == '@')
540 		o = options_add(oo, name);
541 	else 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_STRING(o))
548 		fatalx("option %s is not a string", name);
549 	free(o->string);
550 	o->string = value;
551 	return (o);
552 }
553 
554 struct options_entry *
555 options_set_number(struct options *oo, const char *name, long long value)
556 {
557 	struct options_entry	*o;
558 
559 	if (*name == '@')
560 		fatalx("user option %s must be a string", name);
561 
562 	o = options_get_only(oo, name);
563 	if (o == NULL) {
564 		o = options_default(oo, options_parent_table_entry(oo, name));
565 		if (o == NULL)
566 			return (NULL);
567 	}
568 
569 	if (!OPTIONS_IS_NUMBER(o))
570 		fatalx("option %s is not a number", name);
571 	o->number = value;
572 	return (o);
573 }
574 
575 struct options_entry *
576 options_set_style(struct options *oo, const char *name, int append,
577     const char *value)
578 {
579 	struct options_entry	*o;
580 	struct grid_cell	 gc;
581 
582 	if (*name == '@')
583 		fatalx("user option %s must be a string", name);
584 
585 	o = options_get_only(oo, name);
586 	if (o != NULL && append && OPTIONS_IS_STYLE(o))
587 		memcpy(&gc, &o->style, sizeof gc);
588 	else
589 		memcpy(&gc, &grid_default_cell, sizeof gc);
590 	if (style_parse(&grid_default_cell, &gc, value) == -1)
591 		return (NULL);
592 	if (o == NULL) {
593 		o = options_default(oo, options_parent_table_entry(oo, name));
594 		if (o == NULL)
595 			return (NULL);
596 	}
597 
598 	if (!OPTIONS_IS_STYLE(o))
599 		fatalx("option %s is not a style", name);
600 	memcpy(&o->style, &gc, sizeof o->style);
601 	return (o);
602 }
603 
604 enum options_table_scope
605 options_scope_from_flags(struct args *args, int window,
606     struct cmd_find_state *fs, struct options **oo, char **cause)
607 {
608 	struct session	*s = fs->s;
609 	struct winlink	*wl = fs->wl;
610 	const char	*target= args_get(args, 't');
611 
612 	if (args_has(args, 's')) {
613 		*oo = global_options;
614 		return (OPTIONS_TABLE_SERVER);
615 	}
616 
617 	if (window || args_has(args, 'w')) {
618 		if (args_has(args, 'g')) {
619 			*oo = global_w_options;
620 			return (OPTIONS_TABLE_WINDOW);
621 		}
622 		if (wl == NULL) {
623 			if (target != NULL)
624 				xasprintf(cause, "no such window: %s", target);
625 			else
626 				xasprintf(cause, "no current window");
627 			return (OPTIONS_TABLE_NONE);
628 		}
629 		*oo = wl->window->options;
630 		return (OPTIONS_TABLE_WINDOW);
631 	} else {
632 		if (args_has(args, 'g')) {
633 			*oo = global_s_options;
634 			return (OPTIONS_TABLE_SESSION);
635 		}
636 		if (s == NULL) {
637 			if (target != NULL)
638 				xasprintf(cause, "no such session: %s", target);
639 			else
640 				xasprintf(cause, "no current session");
641 			return (OPTIONS_TABLE_NONE);
642 		}
643 		*oo = s->options;
644 		return (OPTIONS_TABLE_SESSION);
645 	}
646 }
647 
648 void
649 options_style_update_new(struct options *oo, struct options_entry *o)
650 {
651 	const char		*newname = o->tableentry->style;
652 	struct options_entry	*new;
653 
654 	if (newname == NULL)
655 		return;
656 	new = options_get_only(oo, newname);
657 	if (new == NULL)
658 		new = options_set_style(oo, newname, 0, "default");
659 
660 	if (strstr(o->name, "-bg") != NULL)
661 		new->style.bg = o->number;
662 	else if (strstr(o->name, "-fg") != NULL)
663 		new->style.fg = o->number;
664 	else if (strstr(o->name, "-attr") != NULL)
665 		new->style.attr = o->number;
666 }
667 
668 void
669 options_style_update_old(struct options *oo, struct options_entry *o)
670 {
671 	char	newname[128];
672 	int	size;
673 
674 	size = strrchr(o->name, '-') - o->name;
675 
676 	xsnprintf(newname, sizeof newname, "%.*s-bg", size, o->name);
677 	if (options_get(oo, newname) != NULL)
678 		options_set_number(oo, newname, o->style.bg);
679 
680 	xsnprintf(newname, sizeof newname, "%.*s-fg", size, o->name);
681 	if (options_get(oo, newname) != NULL)
682 		options_set_number(oo, newname, o->style.fg);
683 
684 	xsnprintf(newname, sizeof newname, "%.*s-attr", size, o->name);
685 	if (options_get(oo, newname) != NULL)
686 		options_set_number(oo, newname, o->style.attr);
687 }
688