xref: /openbsd-src/usr.bin/tmux/options.c (revision d59bb9942320b767f2a19aaa7690c8c6e30b724c)
1 /* $OpenBSD: options.c,v 1.32 2017/01/30 21:41:17 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 	namelen = strlen(name);
431 
432 	if (*name == '@') {
433 		*ambiguous = 0;
434 		return (xstrdup(name));
435 	}
436 
437 	found = NULL;
438 	for (oe = options_table; oe->name != NULL; oe++) {
439 		if (strcmp(oe->name, name) == 0) {
440 			found = oe;
441 			break;
442 		}
443 		if (strncmp(oe->name, name, namelen) == 0) {
444 			if (found != NULL) {
445 				*ambiguous = 1;
446 				free(name);
447 				return (NULL);
448 			}
449 			found = oe;
450 		}
451 	}
452 	free(name);
453 	if (found == NULL) {
454 		*ambiguous = 0;
455 		return (NULL);
456 	}
457 	return (xstrdup(found->name));
458 }
459 
460 struct options_entry *
461 options_match_get(struct options *oo, const char *s, int *idx, int only,
462     int* ambiguous)
463 {
464 	char			*name;
465 	struct options_entry	*o;
466 
467 	name = options_match(s, idx, ambiguous);
468 	if (name == NULL)
469 		return (NULL);
470 	*ambiguous = 0;
471 	if (only)
472 		o = options_get_only(oo, name);
473 	else
474 		o = options_get(oo, name);
475 	free(name);
476 	return (o);
477 }
478 
479 
480 const char *
481 options_get_string(struct options *oo, const char *name)
482 {
483 	struct options_entry	*o;
484 
485 	o = options_get(oo, name);
486 	if (o == NULL)
487 		fatalx("missing option %s", name);
488 	if (!OPTIONS_IS_STRING(o))
489 		fatalx("option %s is not a string", name);
490 	return (o->string);
491 }
492 
493 long long
494 options_get_number(struct options *oo, const char *name)
495 {
496 	struct options_entry	*o;
497 
498 	o = options_get(oo, name);
499 	if (o == NULL)
500 		fatalx("missing option %s", name);
501 	if (!OPTIONS_IS_NUMBER(o))
502 	    fatalx("option %s is not a number", name);
503 	return (o->number);
504 }
505 
506 const struct grid_cell *
507 options_get_style(struct options *oo, const char *name)
508 {
509 	struct options_entry	*o;
510 
511 	o = options_get(oo, name);
512 	if (o == NULL)
513 		fatalx("missing option %s", name);
514 	if (!OPTIONS_IS_STYLE(o))
515 		fatalx("option %s is not a style", name);
516 	return (&o->style);
517 }
518 
519 struct options_entry *
520 options_set_string(struct options *oo, const char *name, int append,
521     const char *fmt, ...)
522 {
523 	struct options_entry	*o;
524 	va_list			 ap;
525 	char			*s, *value;
526 
527 	va_start(ap, fmt);
528 	xvasprintf(&s, fmt, ap);
529 	va_end(ap);
530 
531 	o = options_get_only(oo, name);
532 	if (o != NULL && append && OPTIONS_IS_STRING(o)) {
533 		xasprintf(&value, "%s%s", o->string, s);
534 		free(s);
535 	} else
536 		value = s;
537 	if (o == NULL && *name == '@')
538 		o = options_add(oo, name);
539 	else if (o == NULL) {
540 		o = options_default(oo, options_parent_table_entry(oo, name));
541 		if (o == NULL)
542 			return (NULL);
543 	}
544 
545 	if (!OPTIONS_IS_STRING(o))
546 		fatalx("option %s is not a string", name);
547 	free(o->string);
548 	o->string = value;
549 	return (o);
550 }
551 
552 struct options_entry *
553 options_set_number(struct options *oo, const char *name, long long value)
554 {
555 	struct options_entry	*o;
556 
557 	if (*name == '@')
558 		fatalx("user option %s must be a string", name);
559 
560 	o = options_get_only(oo, name);
561 	if (o == NULL) {
562 		o = options_default(oo, options_parent_table_entry(oo, name));
563 		if (o == NULL)
564 			return (NULL);
565 	}
566 
567 	if (!OPTIONS_IS_NUMBER(o))
568 		fatalx("option %s is not a number", name);
569 	o->number = value;
570 	return (o);
571 }
572 
573 struct options_entry *
574 options_set_style(struct options *oo, const char *name, int append,
575     const char *value)
576 {
577 	struct options_entry	*o;
578 	struct grid_cell	 gc;
579 
580 	if (*name == '@')
581 		fatalx("user option %s must be a string", name);
582 
583 	o = options_get_only(oo, name);
584 	if (o != NULL && append && OPTIONS_IS_STYLE(o))
585 		memcpy(&gc, &o->style, sizeof gc);
586 	else
587 		memcpy(&gc, &grid_default_cell, sizeof gc);
588 	if (style_parse(&grid_default_cell, &gc, value) == -1)
589 		return (NULL);
590 	if (o == NULL) {
591 		o = options_default(oo, options_parent_table_entry(oo, name));
592 		if (o == NULL)
593 			return (NULL);
594 	}
595 
596 	if (!OPTIONS_IS_STYLE(o))
597 		fatalx("option %s is not a style", name);
598 	memcpy(&o->style, &gc, sizeof o->style);
599 	return (o);
600 }
601 
602 enum options_table_scope
603 options_scope_from_flags(struct args *args, int window,
604     struct cmd_find_state *fs, struct options **oo, char **cause)
605 {
606 	struct session	*s = fs->s;
607 	struct winlink	*wl = fs->wl;
608 	const char	*target= args_get(args, 't');
609 
610 	if (args_has(args, 's')) {
611 		*oo = global_options;
612 		return (OPTIONS_TABLE_SERVER);
613 	}
614 
615 	if (window || args_has(args, 'w')) {
616 		if (args_has(args, 'g')) {
617 			*oo = global_w_options;
618 			return (OPTIONS_TABLE_WINDOW);
619 		}
620 		if (wl == NULL) {
621 			if (target != NULL)
622 				xasprintf(cause, "no such window: %s", target);
623 			else
624 				xasprintf(cause, "no current window");
625 			return (OPTIONS_TABLE_NONE);
626 		}
627 		*oo = wl->window->options;
628 		return (OPTIONS_TABLE_WINDOW);
629 	} else {
630 		if (args_has(args, 'g')) {
631 			*oo = global_s_options;
632 			return (OPTIONS_TABLE_SESSION);
633 		}
634 		if (s == NULL) {
635 			if (target != NULL)
636 				xasprintf(cause, "no such session: %s", target);
637 			else
638 				xasprintf(cause, "no current session");
639 			return (OPTIONS_TABLE_NONE);
640 		}
641 		*oo = s->options;
642 		return (OPTIONS_TABLE_SESSION);
643 	}
644 }
645 
646 void
647 options_style_update_new(struct options *oo, struct options_entry *o)
648 {
649 	const char		*newname = o->tableentry->style;
650 	struct options_entry	*new;
651 
652 	if (newname == NULL)
653 		return;
654 	new = options_get_only(oo, newname);
655 	if (new == NULL)
656 		new = options_set_style(oo, newname, 0, "default");
657 
658 	if (strstr(o->name, "-bg") != NULL)
659 		new->style.bg = o->number;
660 	else if (strstr(o->name, "-fg") != NULL)
661 		new->style.fg = o->number;
662 	else if (strstr(o->name, "-attr") != NULL)
663 		new->style.attr = o->number;
664 }
665 
666 void
667 options_style_update_old(struct options *oo, struct options_entry *o)
668 {
669 	char	newname[128];
670 	int	size;
671 
672 	size = strrchr(o->name, '-') - o->name;
673 
674 	xsnprintf(newname, sizeof newname, "%.*s-bg", size, o->name);
675 	if (options_get(oo, newname) != NULL)
676 		options_set_number(oo, newname, o->style.bg);
677 
678 	xsnprintf(newname, sizeof newname, "%.*s-fg", size, o->name);
679 	if (options_get(oo, newname) != NULL)
680 		options_set_number(oo, newname, o->style.fg);
681 
682 	xsnprintf(newname, sizeof newname, "%.*s-attr", size, o->name);
683 	if (options_get(oo, newname) != NULL)
684 		options_set_number(oo, newname, o->style.attr);
685 }
686