GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/tmux/options.c Lines: 90 310 29.0 %
Date: 2017-11-07 Branches: 120 437 27.5 %

Line Branch Exec Source
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
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








































72470
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
16340
	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
30
	oo = xcalloc(1, sizeof *oo);
107
15
	RB_INIT(&oo->tree);
108
15
	oo->parent = parent;
109
15
	return (oo);
110
}
111
112
void
113
options_free(struct options *oo)
114
{
115
	struct options_entry	*o, *tmp;
116
117
1965
	RB_FOREACH_SAFE(o, options_tree, &oo->tree, tmp)
118
640
		options_remove(o);
119
15
	free(oo);
120
15
}
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
1290
	struct options_entry	o;
138
139
645
	o.name = name;
140
1290
	return (RB_FIND(options_tree, &oo->tree, &o));
141
645
}
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
1280
	o = options_add(oo, oe->name);
164
640
	o->tableentry = oe;
165
166
640
	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
1280
	o = options_empty(oo, oe);
175
640
	if (oe->type == OPTIONS_TABLE_ARRAY)
176
20
		options_array_assign(o, oe->default_str);
177
620
	else if (oe->type == OPTIONS_TABLE_STRING)
178
75
		o->string = xstrdup(oe->default_str);
179
545
	else if (oe->type == OPTIONS_TABLE_STYLE) {
180
75
		memcpy(&o->style, &grid_default_cell, sizeof o->style);
181
75
		style_parse(&grid_default_cell, &o->style, oe->default_str);
182
75
	} else
183
470
		o->number = oe->default_num;
184
640
	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
1280
	o = options_get_only(oo, name);
193
640
	if (o != NULL)
194
		options_remove(o);
195
196
640
	o = xcalloc(1, sizeof *o);
197
640
	o->owner = oo;
198
640
	o->name = xstrdup(name);
199
200
640
	RB_INSERT(options_tree, &oo->tree, o);
201
640
	return (o);
202
}
203
204
void
205
options_remove(struct options_entry *o)
206
{
207
1280
	struct options	*oo = o->owner;
208
	u_int		 i;
209
210

1280
	if (OPTIONS_IS_STRING(o))
211
75
		free((void *)o->string);
212

1130
	else if (OPTIONS_IS_ARRAY(o)) {
213
190
		for (i = 0; i < o->arraysize; i++)
214
75
			free((void *)o->array[i]);
215
20
		free(o->array);
216
20
	}
217
218
640
	RB_REMOVE(options_tree, &oo->tree, o);
219
640
	free(o);
220
640
}
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
150
	char	*new;
256
	u_int	 i;
257
258

150
	if (!OPTIONS_IS_ARRAY(o))
259
		return (-1);
260
261
75
	if (idx >= OPTIONS_ARRAY_LIMIT)
262
		return (-1);
263
75
	if (idx >= o->arraysize) {
264
75
		o->array = xreallocarray(o->array, idx + 1, sizeof *o->array);
265
300
		for (i = o->arraysize; i < idx + 1; i++)
266
75
			o->array[i] = NULL;
267
75
		o->arraysize = idx + 1;
268
75
	}
269
270
75
	new = NULL;
271
75
	if (value != NULL) {
272
75
		if (o->array[idx] != NULL && append)
273
			xasprintf(&new, "%s%s", o->array[idx], value);
274
		else
275
75
			new = xstrdup(value);
276
	}
277
278
75
	free((void *)o->array[idx]);
279
75
	o->array[idx] = new;
280
75
	return (0);
281
75
}
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
40
	char		*copy, *next, *string;
298
	u_int		 i;
299
300
20
	separator = o->tableentry->separator;
301
20
	if (separator == NULL)
302
		separator = " ,";
303
304
20
	copy = string = xstrdup(s);
305
195
	while ((next = strsep(&string, separator)) != NULL) {
306
80
		if (*next == '\0')
307
			continue;
308
520
		for (i = 0; i < OPTIONS_ARRAY_LIMIT; i++) {
309

445
			if (i >= o->arraysize || o->array[i] == NULL)
310
				break;
311
		}
312
75
		if (i == OPTIONS_ARRAY_LIMIT)
313
			break;
314
75
		options_array_set(o, i, next, 0);
315
	}
316
20
	free(copy);
317
20
}
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
const char *
482
options_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
494
long long
495
options_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
507
const struct grid_cell *
508
options_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
520
struct options_entry *
521
options_set_string(struct options *oo, const char *name, int append,
522
    const char *fmt, ...)
523
{
524
	struct options_entry	*o;
525
10
	va_list			 ap;
526
5
	char			*s, *value;
527
528
5
	va_start(ap, fmt);
529
5
	xvasprintf(&s, fmt, ap);
530
5
	va_end(ap);
531
532
5
	o = options_get_only(oo, name);
533

5
	if (o != NULL && append && OPTIONS_IS_STRING(o)) {
534
		xasprintf(&value, "%s%s", o->string, s);
535
		free(s);
536
	} else
537
5
		value = s;
538

5
	if (o == NULL && *name == '@')
539
		o = options_add(oo, name);
540
5
	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

10
	if (!OPTIONS_IS_STRING(o))
547
		fatalx("option %s is not a string", name);
548
5
	free(o->string);
549
5
	o->string = value;
550
5
	return (o);
551
5
}
552
553
struct options_entry *
554
options_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
574
struct options_entry *
575
options_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
603
enum options_table_scope
604
options_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
647
void
648
options_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
667
void
668
options_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
}