xref: /openbsd-src/usr.bin/tmux/options.c (revision 03adc85b7600a1f8f04886b8321c1c1c0c4933d4)
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 
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 	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 
198 static struct options_entry *
199 options_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 
215 void
216 options_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 
233 const char *
234 options_name(struct options_entry *o)
235 {
236 	return (o->name);
237 }
238 
239 const struct options_table_entry *
240 options_table_entry(struct options_entry *o)
241 {
242 	return (o->tableentry);
243 }
244 
245 const char *
246 options_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 
255 int
256 options_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 
280 int
281 options_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 
290 int
291 options_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 
298 const char *
299 options_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 
348 char *
349 options_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 
373 struct options_entry *
374 options_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 
396 char *
397 options_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 
434 struct options_entry *
435 options_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 
460 const char *
461 options_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 
473 long long
474 options_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 
486 const struct grid_cell *
487 options_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 
499 struct options_entry *
500 options_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 
532 struct options_entry *
533 options_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 
553 struct options_entry *
554 options_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 
582 enum options_table_scope
583 options_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 
626 void
627 options_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 
646 void
647 options_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