Coverage Report

Created: 2025-08-29 06:28

/src/tmux/options.c
Line
Count
Source (jump to first uncovered line)
1
/* $OpenBSD$ */
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 <fnmatch.h>
23
#include <stdarg.h>
24
#include <stdlib.h>
25
#include <string.h>
26
27
#include "tmux.h"
28
29
/*
30
 * Option handling; each option has a name, type and value and is stored in
31
 * a red-black tree.
32
 */
33
34
struct options_array_item {
35
  u_int        index;
36
  union options_value    value;
37
  RB_ENTRY(options_array_item)   entry;
38
};
39
static int
40
options_array_cmp(struct options_array_item *a1, struct options_array_item *a2)
41
464
{
42
464
  if (a1->index < a2->index)
43
36
    return (-1);
44
428
  if (a1->index > a2->index)
45
320
    return (1);
46
108
  return (0);
47
428
}
48
RB_GENERATE_STATIC(options_array, options_array_item, entry, options_array_cmp);
49
50
struct options_entry {
51
  struct options        *owner;
52
53
  const char        *name;
54
  const struct options_table_entry  *tableentry;
55
  union options_value      value;
56
57
  int          cached;
58
  struct style         style;
59
60
  RB_ENTRY(options_entry)      entry;
61
};
62
63
struct options {
64
  RB_HEAD(options_tree, options_entry)   tree;
65
  struct options        *parent;
66
};
67
68
static struct options_entry *options_add(struct options *, const char *);
69
static void      options_remove(struct options_entry *);
70
71
#define OPTIONS_IS_STRING(o)            \
72
28.1k
  ((o)->tableentry == NULL ||         \
73
28.1k
      (o)->tableentry->type == OPTIONS_TABLE_STRING)
74
#define OPTIONS_IS_NUMBER(o) \
75
105k
  ((o)->tableentry != NULL &&         \
76
105k
      ((o)->tableentry->type == OPTIONS_TABLE_NUMBER ||   \
77
105k
      (o)->tableentry->type == OPTIONS_TABLE_KEY ||   \
78
105k
      (o)->tableentry->type == OPTIONS_TABLE_COLOUR ||   \
79
105k
      (o)->tableentry->type == OPTIONS_TABLE_FLAG ||   \
80
105k
      (o)->tableentry->type == OPTIONS_TABLE_CHOICE))
81
#define OPTIONS_IS_COMMAND(o) \
82
506
  ((o)->tableentry != NULL &&         \
83
506
      (o)->tableentry->type == OPTIONS_TABLE_COMMAND)
84
85
#define OPTIONS_IS_ARRAY(o)           \
86
10.6k
  ((o)->tableentry != NULL &&         \
87
10.6k
      ((o)->tableentry->flags & OPTIONS_TABLE_IS_ARRAY))
88
89
static int  options_cmp(struct options_entry *, struct options_entry *);
90
RB_GENERATE_STATIC(options_tree, options_entry, entry, options_cmp);
91
92
static int
93
options_cmp(struct options_entry *lhs, struct options_entry *rhs)
94
734k
{
95
734k
  return (strcmp(lhs->name, rhs->name));
96
734k
}
97
98
static const char *
99
options_map_name(const char *name)
100
180k
{
101
180k
  const struct options_name_map *map;
102
103
1.26M
  for (map = options_other_names; map->from != NULL; map++) {
104
1.08M
    if (strcmp(map->from, name) == 0)
105
0
      return (map->to);
106
1.08M
  }
107
180k
  return (name);
108
180k
}
109
110
static const struct options_table_entry *
111
options_parent_table_entry(struct options *oo, const char *s)
112
231
{
113
231
  struct options_entry  *o;
114
115
231
  if (oo->parent == NULL)
116
0
    fatalx("no parent options for %s", s);
117
231
  o = options_get(oo->parent, s);
118
231
  if (o == NULL)
119
0
    fatalx("%s not in parent options", s);
120
231
  return (o->tableentry);
121
231
}
122
123
static void
124
options_value_free(struct options_entry *o, union options_value *ov)
125
231
{
126
231
  if (OPTIONS_IS_STRING(o))
127
0
    free(ov->string);
128
231
  if (OPTIONS_IS_COMMAND(o) && ov->cmdlist != NULL)
129
0
    cmd_list_free(ov->cmdlist);
130
231
}
131
132
static char *
133
options_value_to_string(struct options_entry *o, union options_value *ov,
134
    int numeric)
135
0
{
136
0
  char  *s;
137
138
0
  if (OPTIONS_IS_COMMAND(o))
139
0
    return (cmd_list_print(ov->cmdlist, 0));
140
0
  if (OPTIONS_IS_NUMBER(o)) {
141
0
    switch (o->tableentry->type) {
142
0
    case OPTIONS_TABLE_NUMBER:
143
0
      xasprintf(&s, "%lld", ov->number);
144
0
      break;
145
0
    case OPTIONS_TABLE_KEY:
146
0
      s = xstrdup(key_string_lookup_key(ov->number, 0));
147
0
      break;
148
0
    case OPTIONS_TABLE_COLOUR:
149
0
      s = xstrdup(colour_tostring(ov->number));
150
0
      break;
151
0
    case OPTIONS_TABLE_FLAG:
152
0
      if (numeric)
153
0
        xasprintf(&s, "%lld", ov->number);
154
0
      else
155
0
        s = xstrdup(ov->number ? "on" : "off");
156
0
      break;
157
0
    case OPTIONS_TABLE_CHOICE:
158
0
      s = xstrdup(o->tableentry->choices[ov->number]);
159
0
      break;
160
0
    default:
161
0
      fatalx("not a number option type");
162
0
    }
163
0
    return (s);
164
0
  }
165
0
  if (OPTIONS_IS_STRING(o))
166
0
    return (xstrdup(ov->string));
167
0
  return (xstrdup(""));
168
0
}
169
170
struct options *
171
options_create(struct options *parent)
172
20.8k
{
173
20.8k
  struct options  *oo;
174
175
20.8k
  oo = xcalloc(1, sizeof *oo);
176
20.8k
  RB_INIT(&oo->tree);
177
20.8k
  oo->parent = parent;
178
20.8k
  return (oo);
179
20.8k
}
180
181
void
182
options_free(struct options *oo)
183
20.8k
{
184
20.8k
  struct options_entry  *o, *tmp;
185
186
20.8k
  RB_FOREACH_SAFE(o, options_tree, &oo->tree, tmp)
187
131
    options_remove(o);
188
20.8k
  free(oo);
189
20.8k
}
190
191
struct options *
192
options_get_parent(struct options *oo)
193
0
{
194
0
  return (oo->parent);
195
0
}
196
197
void
198
options_set_parent(struct options *oo, struct options *parent)
199
0
{
200
0
  oo->parent = parent;
201
0
}
202
203
struct options_entry *
204
options_first(struct options *oo)
205
0
{
206
0
  return (RB_MIN(options_tree, &oo->tree));
207
0
}
208
209
struct options_entry *
210
options_next(struct options_entry *o)
211
0
{
212
0
  return (RB_NEXT(options_tree, &oo->tree, o));
213
0
}
214
215
struct options_entry *
216
options_get_only(struct options *oo, const char *name)
217
324k
{
218
324k
  struct options_entry  o = { .name = name }, *found;
219
220
324k
  found = RB_FIND(options_tree, &oo->tree, &o);
221
324k
  if (found == NULL) {
222
180k
    o.name = options_map_name(name);
223
180k
    return (RB_FIND(options_tree, &oo->tree, &o));
224
180k
  }
225
144k
  return (found);
226
324k
}
227
228
struct options_entry *
229
options_get(struct options *oo, const char *name)
230
150k
{
231
150k
  struct options_entry  *o;
232
233
150k
  o = options_get_only(oo, name);
234
321k
  while (o == NULL) {
235
178k
    oo = oo->parent;
236
178k
    if (oo == NULL)
237
6.53k
      break;
238
171k
    o = options_get_only(oo, name);
239
171k
  }
240
150k
  return (o);
241
150k
}
242
243
struct options_entry *
244
options_empty(struct options *oo, const struct options_table_entry *oe)
245
649
{
246
649
  struct options_entry  *o;
247
248
649
  o = options_add(oo, oe->name);
249
649
  o->tableentry = oe;
250
251
649
  if (oe->flags & OPTIONS_TABLE_IS_ARRAY)
252
152
    RB_INIT(&o->value.array);
253
254
649
  return (o);
255
649
}
256
257
struct options_entry *
258
options_default(struct options *oo, const struct options_table_entry *oe)
259
649
{
260
649
  struct options_entry  *o;
261
649
  union options_value *ov;
262
649
  u_int      i;
263
649
  struct cmd_parse_result *pr;
264
265
649
  o = options_empty(oo, oe);
266
649
  ov = &o->value;
267
268
649
  if (oe->flags & OPTIONS_TABLE_IS_ARRAY) {
269
152
    if (oe->default_arr == NULL) {
270
150
      options_array_assign(o, oe->default_str, NULL);
271
150
      return (o);
272
150
    }
273
8
    for (i = 0; oe->default_arr[i] != NULL; i++)
274
6
      options_array_set(o, i, oe->default_arr[i], 0, NULL);
275
2
    return (o);
276
152
  }
277
278
497
  switch (oe->type) {
279
110
  case OPTIONS_TABLE_STRING:
280
110
    ov->string = xstrdup(oe->default_str);
281
110
    break;
282
2
  case OPTIONS_TABLE_COMMAND:
283
2
    pr = cmd_parse_from_string(oe->default_str, NULL);
284
2
    switch (pr->status) {
285
0
    case CMD_PARSE_ERROR:
286
0
      free(pr->error);
287
0
      break;
288
2
    case CMD_PARSE_SUCCESS:
289
2
      ov->cmdlist = pr->cmdlist;
290
2
      break;
291
2
    }
292
2
    break;
293
385
  default:
294
385
    ov->number = oe->default_num;
295
385
    break;
296
497
  }
297
497
  return (o);
298
497
}
299
300
char *
301
options_default_to_string(const struct options_table_entry *oe)
302
0
{
303
0
  char  *s;
304
305
0
  switch (oe->type) {
306
0
  case OPTIONS_TABLE_STRING:
307
0
  case OPTIONS_TABLE_COMMAND:
308
0
    s = xstrdup(oe->default_str);
309
0
    break;
310
0
  case OPTIONS_TABLE_NUMBER:
311
0
    xasprintf(&s, "%lld", oe->default_num);
312
0
    break;
313
0
  case OPTIONS_TABLE_KEY:
314
0
    s = xstrdup(key_string_lookup_key(oe->default_num, 0));
315
0
    break;
316
0
  case OPTIONS_TABLE_COLOUR:
317
0
    s = xstrdup(colour_tostring(oe->default_num));
318
0
    break;
319
0
  case OPTIONS_TABLE_FLAG:
320
0
    s = xstrdup(oe->default_num ? "on" : "off");
321
0
    break;
322
0
  case OPTIONS_TABLE_CHOICE:
323
0
    s = xstrdup(oe->choices[oe->default_num]);
324
0
    break;
325
0
  default:
326
0
    fatalx("unknown option type");
327
0
  }
328
0
  return (s);
329
0
}
330
331
static struct options_entry *
332
options_add(struct options *oo, const char *name)
333
649
{
334
649
  struct options_entry  *o;
335
336
649
  o = options_get_only(oo, name);
337
649
  if (o != NULL)
338
0
    options_remove(o);
339
340
649
  o = xcalloc(1, sizeof *o);
341
649
  o->owner = oo;
342
649
  o->name = xstrdup(name);
343
344
649
  RB_INSERT(options_tree, &oo->tree, o);
345
649
  return (o);
346
649
}
347
348
static void
349
options_remove(struct options_entry *o)
350
231
{
351
231
  struct options  *oo = o->owner;
352
353
231
  if (OPTIONS_IS_ARRAY(o))
354
0
    options_array_clear(o);
355
231
  else
356
231
    options_value_free(o, &o->value);
357
231
  RB_REMOVE(options_tree, &oo->tree, o);
358
231
  free((void *)o->name);
359
231
  free(o);
360
231
}
361
362
const char *
363
options_name(struct options_entry *o)
364
0
{
365
0
  return (o->name);
366
0
}
367
368
struct options *
369
options_owner(struct options_entry *o)
370
0
{
371
0
  return (o->owner);
372
0
}
373
374
const struct options_table_entry *
375
options_table_entry(struct options_entry *o)
376
0
{
377
0
  return (o->tableentry);
378
0
}
379
380
static struct options_array_item *
381
options_array_item(struct options_entry *o, u_int idx)
382
190
{
383
190
  struct options_array_item a;
384
385
190
  a.index = idx;
386
190
  return (RB_FIND(options_array, &o->value.array, &a));
387
190
}
388
389
static struct options_array_item *
390
options_array_new(struct options_entry *o, u_int idx)
391
44
{
392
44
  struct options_array_item *a;
393
394
44
  a = xcalloc(1, sizeof *a);
395
44
  a->index = idx;
396
44
  RB_INSERT(options_array, &o->value.array, a);
397
44
  return (a);
398
44
}
399
400
static void
401
options_array_free(struct options_entry *o, struct options_array_item *a)
402
0
{
403
0
  options_value_free(o, &a->value);
404
0
  RB_REMOVE(options_array, &o->value.array, a);
405
0
  free(a);
406
0
}
407
408
void
409
options_array_clear(struct options_entry *o)
410
0
{
411
0
  struct options_array_item *a, *a1;
412
413
0
  if (!OPTIONS_IS_ARRAY(o))
414
0
    return;
415
416
0
  RB_FOREACH_SAFE(a, options_array, &o->value.array, a1)
417
0
    options_array_free(o, a);
418
0
}
419
420
union options_value *
421
options_array_get(struct options_entry *o, u_int idx)
422
0
{
423
0
  struct options_array_item *a;
424
425
0
  if (!OPTIONS_IS_ARRAY(o))
426
0
    return (NULL);
427
0
  a = options_array_item(o, idx);
428
0
  if (a == NULL)
429
0
    return (NULL);
430
0
  return (&a->value);
431
0
}
432
433
int
434
options_array_set(struct options_entry *o, u_int idx, const char *value,
435
    int append, char **cause)
436
44
{
437
44
  struct options_array_item *a;
438
44
  char        *new;
439
44
  struct cmd_parse_result   *pr;
440
44
  long long      number;
441
442
44
  if (!OPTIONS_IS_ARRAY(o)) {
443
0
    if (cause != NULL)
444
0
      *cause = xstrdup("not an array");
445
0
    return (-1);
446
0
  }
447
448
44
  if (value == NULL) {
449
0
    a = options_array_item(o, idx);
450
0
    if (a != NULL)
451
0
      options_array_free(o, a);
452
0
    return (0);
453
0
  }
454
455
44
  if (OPTIONS_IS_COMMAND(o)) {
456
0
    pr = cmd_parse_from_string(value, NULL);
457
0
    switch (pr->status) {
458
0
    case CMD_PARSE_ERROR:
459
0
      if (cause != NULL)
460
0
        *cause = pr->error;
461
0
      else
462
0
        free(pr->error);
463
0
      return (-1);
464
0
    case CMD_PARSE_SUCCESS:
465
0
      break;
466
0
    }
467
468
0
    a = options_array_item(o, idx);
469
0
    if (a == NULL)
470
0
      a = options_array_new(o, idx);
471
0
    else
472
0
      options_value_free(o, &a->value);
473
0
    a->value.cmdlist = pr->cmdlist;
474
0
    return (0);
475
0
  }
476
477
44
  if (OPTIONS_IS_STRING(o)) {
478
44
    a = options_array_item(o, idx);
479
44
    if (a != NULL && append)
480
0
      xasprintf(&new, "%s%s", a->value.string, value);
481
44
    else
482
44
      new = xstrdup(value);
483
44
    if (a == NULL)
484
44
      a = options_array_new(o, idx);
485
0
    else
486
0
      options_value_free(o, &a->value);
487
44
    a->value.string = new;
488
44
    return (0);
489
44
  }
490
491
0
  if (o->tableentry->type == OPTIONS_TABLE_COLOUR) {
492
0
    if ((number = colour_fromstring(value)) == -1) {
493
0
      xasprintf(cause, "bad colour: %s", value);
494
0
      return (-1);
495
0
    }
496
0
    a = options_array_item(o, idx);
497
0
    if (a == NULL)
498
0
      a = options_array_new(o, idx);
499
0
    else
500
0
      options_value_free(o, &a->value);
501
0
    a->value.number = number;
502
0
    return (0);
503
0
  }
504
505
0
  if (cause != NULL)
506
0
    *cause = xstrdup("wrong array type");
507
0
  return (-1);
508
0
}
509
510
int
511
options_array_assign(struct options_entry *o, const char *s, char **cause)
512
150
{
513
150
  const char  *separator;
514
150
  char    *copy, *next, *string;
515
150
  u_int    i;
516
517
150
  separator = o->tableentry->separator;
518
150
  if (separator == NULL)
519
4
    separator = " ,";
520
150
  if (*separator == '\0') {
521
136
    if (*s == '\0')
522
136
      return (0);
523
0
    for (i = 0; i < UINT_MAX; i++) {
524
0
      if (options_array_item(o, i) == NULL)
525
0
        break;
526
0
    }
527
0
    return (options_array_set(o, i, s, 0, cause));
528
136
  }
529
530
14
  if (*s == '\0')
531
6
    return (0);
532
8
  copy = string = xstrdup(s);
533
46
  while ((next = strsep(&string, separator)) != NULL) {
534
38
    if (*next == '\0')
535
0
      continue;
536
146
    for (i = 0; i < UINT_MAX; i++) {
537
146
      if (options_array_item(o, i) == NULL)
538
38
        break;
539
146
    }
540
38
    if (i == UINT_MAX)
541
0
      break;
542
38
    if (options_array_set(o, i, next, 0, cause) != 0) {
543
0
      free(copy);
544
0
      return (-1);
545
0
    }
546
38
  }
547
8
  free(copy);
548
8
  return (0);
549
8
}
550
551
struct options_array_item *
552
options_array_first(struct options_entry *o)
553
10.4k
{
554
10.4k
  if (!OPTIONS_IS_ARRAY(o))
555
0
    return (NULL);
556
10.4k
  return (RB_MIN(options_array, &o->value.array));
557
10.4k
}
558
559
struct options_array_item *
560
options_array_next(struct options_array_item *a)
561
12
{
562
12
  return (RB_NEXT(options_array, &o->value.array, a));
563
12
}
564
565
u_int
566
options_array_item_index(struct options_array_item *a)
567
0
{
568
0
  return (a->index);
569
0
}
570
571
union options_value *
572
options_array_item_value(struct options_array_item *a)
573
12
{
574
12
  return (&a->value);
575
12
}
576
577
int
578
options_is_array(struct options_entry *o)
579
0
{
580
0
  return (OPTIONS_IS_ARRAY(o));
581
0
}
582
583
int
584
options_is_string(struct options_entry *o)
585
0
{
586
0
  return (OPTIONS_IS_STRING(o));
587
0
}
588
589
char *
590
options_to_string(struct options_entry *o, int idx, int numeric)
591
0
{
592
0
  struct options_array_item *a;
593
0
  char        *result = NULL;
594
0
  char        *last = NULL;
595
0
  char        *next;
596
597
0
  if (OPTIONS_IS_ARRAY(o)) {
598
0
    if (idx == -1) {
599
0
      RB_FOREACH(a, options_array, &o->value.array) {
600
0
        next = options_value_to_string(o, &a->value,
601
0
            numeric);
602
0
        if (last == NULL)
603
0
          result = next;
604
0
        else {
605
0
          xasprintf(&result, "%s %s", last, next);
606
0
          free(last);
607
0
          free(next);
608
0
        }
609
0
        last = result;
610
0
      }
611
0
      if (result == NULL)
612
0
        return (xstrdup(""));
613
0
      return (result);
614
0
    }
615
0
    a = options_array_item(o, idx);
616
0
    if (a == NULL)
617
0
      return (xstrdup(""));
618
0
    return (options_value_to_string(o, &a->value, numeric));
619
0
  }
620
0
  return (options_value_to_string(o, &o->value, numeric));
621
0
}
622
623
char *
624
options_parse(const char *name, int *idx)
625
0
{
626
0
  char  *copy, *cp, *end;
627
628
0
  if (*name == '\0')
629
0
    return (NULL);
630
0
  copy = xstrdup(name);
631
0
  if ((cp = strchr(copy, '[')) == NULL) {
632
0
    *idx = -1;
633
0
    return (copy);
634
0
  }
635
0
  end = strchr(cp + 1, ']');
636
0
  if (end == NULL || end[1] != '\0' || !isdigit((u_char)end[-1])) {
637
0
    free(copy);
638
0
    return (NULL);
639
0
  }
640
0
  if (sscanf(cp, "[%d]", idx) != 1 || *idx < 0) {
641
0
    free(copy);
642
0
    return (NULL);
643
0
  }
644
0
  *cp = '\0';
645
0
  return (copy);
646
0
}
647
648
struct options_entry *
649
options_parse_get(struct options *oo, const char *s, int *idx, int only)
650
0
{
651
0
  struct options_entry  *o;
652
0
  char      *name;
653
654
0
  name = options_parse(s, idx);
655
0
  if (name == NULL)
656
0
    return (NULL);
657
0
  if (only)
658
0
    o = options_get_only(oo, name);
659
0
  else
660
0
    o = options_get(oo, name);
661
0
  free(name);
662
0
  return (o);
663
0
}
664
665
char *
666
options_match(const char *s, int *idx, int *ambiguous)
667
0
{
668
0
  const struct options_table_entry  *oe, *found;
669
0
  char          *parsed;
670
0
  const char        *name;
671
0
  size_t           namelen;
672
673
0
  parsed = options_parse(s, idx);
674
0
  if (parsed == NULL)
675
0
    return (NULL);
676
0
  if (*parsed == '@') {
677
0
    *ambiguous = 0;
678
0
    return (parsed);
679
0
  }
680
681
0
  name = options_map_name(parsed);
682
0
  namelen = strlen(name);
683
684
0
  found = NULL;
685
0
  for (oe = options_table; oe->name != NULL; oe++) {
686
0
    if (strcmp(oe->name, name) == 0) {
687
0
      found = oe;
688
0
      break;
689
0
    }
690
0
    if (strncmp(oe->name, name, namelen) == 0) {
691
0
      if (found != NULL) {
692
0
        *ambiguous = 1;
693
0
        free(parsed);
694
0
        return (NULL);
695
0
      }
696
0
      found = oe;
697
0
    }
698
0
  }
699
0
  free(parsed);
700
0
  if (found == NULL) {
701
0
    *ambiguous = 0;
702
0
    return (NULL);
703
0
  }
704
0
  return (xstrdup(found->name));
705
0
}
706
707
struct options_entry *
708
options_match_get(struct options *oo, const char *s, int *idx, int only,
709
    int *ambiguous)
710
0
{
711
0
  char      *name;
712
0
  struct options_entry  *o;
713
714
0
  name = options_match(s, idx, ambiguous);
715
0
  if (name == NULL)
716
0
    return (NULL);
717
0
  *ambiguous = 0;
718
0
  if (only)
719
0
    o = options_get_only(oo, name);
720
0
  else
721
0
    o = options_get(oo, name);
722
0
  free(name);
723
0
  return (o);
724
0
}
725
726
const char *
727
options_get_string(struct options *oo, const char *name)
728
10.4k
{
729
10.4k
  struct options_entry  *o;
730
731
10.4k
  o = options_get(oo, name);
732
10.4k
  if (o == NULL)
733
0
    fatalx("missing option %s", name);
734
10.4k
  if (!OPTIONS_IS_STRING(o))
735
0
    fatalx("option %s is not a string", name);
736
10.4k
  return (o->value.string);
737
10.4k
}
738
739
long long
740
options_get_number(struct options *oo, const char *name)
741
105k
{
742
105k
  struct options_entry  *o;
743
744
105k
  o = options_get(oo, name);
745
105k
  if (o == NULL)
746
0
    fatalx("missing option %s", name);
747
105k
  if (!OPTIONS_IS_NUMBER(o))
748
0
    fatalx("option %s is not a number", name);
749
105k
  return (o->value.number);
750
105k
}
751
752
const struct cmd_list *
753
options_get_command(struct options *oo, const char *name)
754
0
{
755
0
  struct options_entry  *o;
756
757
0
  o = options_get(oo, name);
758
0
  if (o == NULL)
759
0
    fatalx("missing option %s", name);
760
0
  if (!OPTIONS_IS_COMMAND(o))
761
0
    fatalx("option %s is not a command", name);
762
0
  return (o->value.cmdlist);
763
0
}
764
765
struct options_entry *
766
options_set_string(struct options *oo, const char *name, int append,
767
    const char *fmt, ...)
768
0
{
769
0
  struct options_entry  *o;
770
0
  va_list      ap;
771
0
  const char    *separator = "";
772
0
  char      *s, *value;
773
774
0
  va_start(ap, fmt);
775
0
  xvasprintf(&s, fmt, ap);
776
0
  va_end(ap);
777
778
0
  o = options_get_only(oo, name);
779
0
  if (o != NULL && append && OPTIONS_IS_STRING(o)) {
780
0
    if (*name != '@') {
781
0
      separator = o->tableentry->separator;
782
0
      if (separator == NULL)
783
0
        separator = "";
784
0
    }
785
0
    xasprintf(&value, "%s%s%s", o->value.string, separator, s);
786
0
    free(s);
787
0
  } else
788
0
    value = s;
789
0
  if (o == NULL && *name == '@')
790
0
    o = options_add(oo, name);
791
0
  else if (o == NULL) {
792
0
    o = options_default(oo, options_parent_table_entry(oo, name));
793
0
    if (o == NULL)
794
0
      return (NULL);
795
0
  }
796
797
0
  if (!OPTIONS_IS_STRING(o))
798
0
    fatalx("option %s is not a string", name);
799
0
  free(o->value.string);
800
0
  o->value.string = value;
801
0
  o->cached = 0;
802
0
  return (o);
803
0
}
804
805
struct options_entry *
806
options_set_number(struct options *oo, const char *name, long long value)
807
612
{
808
612
  struct options_entry  *o;
809
810
612
  if (*name == '@')
811
0
    fatalx("user option %s must be a string", name);
812
813
612
  o = options_get_only(oo, name);
814
612
  if (o == NULL) {
815
231
    o = options_default(oo, options_parent_table_entry(oo, name));
816
231
    if (o == NULL)
817
0
      return (NULL);
818
231
  }
819
820
612
  if (!OPTIONS_IS_NUMBER(o))
821
0
    fatalx("option %s is not a number", name);
822
612
  o->value.number = value;
823
612
  return (o);
824
612
}
825
826
struct options_entry *
827
options_set_command(struct options *oo, const char *name,
828
    struct cmd_list *value)
829
0
{
830
0
  struct options_entry  *o;
831
832
0
  if (*name == '@')
833
0
    fatalx("user option %s must be a string", name);
834
835
0
  o = options_get_only(oo, name);
836
0
  if (o == NULL) {
837
0
    o = options_default(oo, options_parent_table_entry(oo, name));
838
0
    if (o == NULL)
839
0
      return (NULL);
840
0
  }
841
842
0
  if (!OPTIONS_IS_COMMAND(o))
843
0
    fatalx("option %s is not a command", name);
844
0
  if (o->value.cmdlist != NULL)
845
0
    cmd_list_free(o->value.cmdlist);
846
0
  o->value.cmdlist = value;
847
0
  return (o);
848
0
}
849
850
int
851
options_scope_from_name(struct args *args, int window,
852
    const char *name, struct cmd_find_state *fs, struct options **oo,
853
    char **cause)
854
0
{
855
0
  struct session        *s = fs->s;
856
0
  struct winlink        *wl = fs->wl;
857
0
  struct window_pane      *wp = fs->wp;
858
0
  const char        *target = args_get(args, 't');
859
0
  const struct options_table_entry  *oe;
860
0
  int          scope = OPTIONS_TABLE_NONE;
861
862
0
  if (*name == '@')
863
0
    return (options_scope_from_flags(args, window, fs, oo, cause));
864
865
0
  for (oe = options_table; oe->name != NULL; oe++) {
866
0
    if (strcmp(oe->name, name) == 0)
867
0
      break;
868
0
  }
869
0
  if (oe->name == NULL) {
870
0
    xasprintf(cause, "unknown option: %s", name);
871
0
    return (OPTIONS_TABLE_NONE);
872
0
  }
873
0
  switch (oe->scope) {
874
0
  case OPTIONS_TABLE_SERVER:
875
0
    *oo = global_options;
876
0
    scope = OPTIONS_TABLE_SERVER;
877
0
    break;
878
0
  case OPTIONS_TABLE_SESSION:
879
0
    if (args_has(args, 'g')) {
880
0
      *oo = global_s_options;
881
0
      scope = OPTIONS_TABLE_SESSION;
882
0
    } else if (s == NULL && target != NULL)
883
0
      xasprintf(cause, "no such session: %s", target);
884
0
    else if (s == NULL)
885
0
      xasprintf(cause, "no current session");
886
0
    else {
887
0
      *oo = s->options;
888
0
      scope = OPTIONS_TABLE_SESSION;
889
0
    }
890
0
    break;
891
0
  case OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE:
892
0
    if (args_has(args, 'p')) {
893
0
      if (wp == NULL && target != NULL)
894
0
        xasprintf(cause, "no such pane: %s", target);
895
0
      else if (wp == NULL)
896
0
        xasprintf(cause, "no current pane");
897
0
      else {
898
0
        *oo = wp->options;
899
0
        scope = OPTIONS_TABLE_PANE;
900
0
      }
901
0
      break;
902
0
    }
903
    /* FALLTHROUGH */
904
0
  case OPTIONS_TABLE_WINDOW:
905
0
    if (args_has(args, 'g')) {
906
0
      *oo = global_w_options;
907
0
      scope = OPTIONS_TABLE_WINDOW;
908
0
    } else if (wl == NULL && target != NULL)
909
0
      xasprintf(cause, "no such window: %s", target);
910
0
    else if (wl == NULL)
911
0
      xasprintf(cause, "no current window");
912
0
    else {
913
0
      *oo = wl->window->options;
914
0
      scope = OPTIONS_TABLE_WINDOW;
915
0
    }
916
0
    break;
917
0
  }
918
0
  return (scope);
919
0
}
920
921
int
922
options_scope_from_flags(struct args *args, int window,
923
    struct cmd_find_state *fs, struct options **oo, char **cause)
924
0
{
925
0
  struct session    *s = fs->s;
926
0
  struct winlink    *wl = fs->wl;
927
0
  struct window_pane  *wp = fs->wp;
928
0
  const char    *target = args_get(args, 't');
929
930
0
  if (args_has(args, 's')) {
931
0
    *oo = global_options;
932
0
    return (OPTIONS_TABLE_SERVER);
933
0
  }
934
935
0
  if (args_has(args, 'p')) {
936
0
    if (wp == NULL) {
937
0
      if (target != NULL)
938
0
        xasprintf(cause, "no such pane: %s", target);
939
0
      else
940
0
        xasprintf(cause, "no current pane");
941
0
      return (OPTIONS_TABLE_NONE);
942
0
    }
943
0
    *oo = wp->options;
944
0
    return (OPTIONS_TABLE_PANE);
945
0
  } else if (window || args_has(args, 'w')) {
946
0
    if (args_has(args, 'g')) {
947
0
      *oo = global_w_options;
948
0
      return (OPTIONS_TABLE_WINDOW);
949
0
    }
950
0
    if (wl == NULL) {
951
0
      if (target != NULL)
952
0
        xasprintf(cause, "no such window: %s", target);
953
0
      else
954
0
        xasprintf(cause, "no current window");
955
0
      return (OPTIONS_TABLE_NONE);
956
0
    }
957
0
    *oo = wl->window->options;
958
0
    return (OPTIONS_TABLE_WINDOW);
959
0
  } else {
960
0
    if (args_has(args, 'g')) {
961
0
      *oo = global_s_options;
962
0
      return (OPTIONS_TABLE_SESSION);
963
0
    }
964
0
    if (s == NULL) {
965
0
      if (target != NULL)
966
0
        xasprintf(cause, "no such session: %s", target);
967
0
      else
968
0
        xasprintf(cause, "no current session");
969
0
      return (OPTIONS_TABLE_NONE);
970
0
    }
971
0
    *oo = s->options;
972
0
    return (OPTIONS_TABLE_SESSION);
973
0
  }
974
0
}
975
976
struct style *
977
options_string_to_style(struct options *oo, const char *name,
978
    struct format_tree *ft)
979
17.4k
{
980
17.4k
  struct options_entry  *o;
981
17.4k
  const char    *s;
982
17.4k
  char      *expanded;
983
984
17.4k
  o = options_get(oo, name);
985
17.4k
  if (o == NULL || !OPTIONS_IS_STRING(o))
986
0
    return (NULL);
987
988
17.4k
  if (o->cached)
989
17.4k
    return (&o->style);
990
3
  s = o->value.string;
991
3
  log_debug("%s: %s is '%s'", __func__, name, s);
992
993
3
  style_set(&o->style, &grid_default_cell);
994
3
  o->cached = (strstr(s, "#{") == NULL);
995
996
3
  if (ft != NULL && !o->cached) {
997
0
    expanded = format_expand(ft, s);
998
0
    if (style_parse(&o->style, &grid_default_cell, expanded) != 0) {
999
0
      free(expanded);
1000
0
      return (NULL);
1001
0
    }
1002
0
    free(expanded);
1003
3
  } else {
1004
3
    if (style_parse(&o->style, &grid_default_cell, s) != 0)
1005
0
      return (NULL);
1006
3
  }
1007
3
  return (&o->style);
1008
3
}
1009
1010
static int
1011
options_from_string_check(const struct options_table_entry *oe,
1012
    const char *value, char **cause)
1013
0
{
1014
0
  struct style  sy;
1015
1016
0
  if (oe == NULL)
1017
0
    return (0);
1018
0
  if (strcmp(oe->name, "default-shell") == 0 && !checkshell(value)) {
1019
0
    xasprintf(cause, "not a suitable shell: %s", value);
1020
0
    return (-1);
1021
0
  }
1022
0
  if (oe->pattern != NULL && fnmatch(oe->pattern, value, 0) != 0) {
1023
0
    xasprintf(cause, "value is invalid: %s", value);
1024
0
    return (-1);
1025
0
  }
1026
0
  if ((oe->flags & OPTIONS_TABLE_IS_STYLE) &&
1027
0
      strstr(value, "#{") == NULL &&
1028
0
      style_parse(&sy, &grid_default_cell, value) != 0) {
1029
0
    xasprintf(cause, "invalid style: %s", value);
1030
0
    return (-1);
1031
0
  }
1032
0
  return (0);
1033
0
}
1034
1035
static int
1036
options_from_string_flag(struct options *oo, const char *name,
1037
    const char *value, char **cause)
1038
0
{
1039
0
  int flag;
1040
1041
0
  if (value == NULL || *value == '\0')
1042
0
    flag = !options_get_number(oo, name);
1043
0
  else if (strcmp(value, "1") == 0 ||
1044
0
      strcasecmp(value, "on") == 0 ||
1045
0
      strcasecmp(value, "yes") == 0)
1046
0
    flag = 1;
1047
0
  else if (strcmp(value, "0") == 0 ||
1048
0
      strcasecmp(value, "off") == 0 ||
1049
0
      strcasecmp(value, "no") == 0)
1050
0
    flag = 0;
1051
0
  else {
1052
0
    xasprintf(cause, "bad value: %s", value);
1053
0
    return (-1);
1054
0
  }
1055
0
  options_set_number(oo, name, flag);
1056
0
  return (0);
1057
0
}
1058
1059
int
1060
options_find_choice(const struct options_table_entry *oe, const char *value,
1061
    char **cause)
1062
0
{
1063
0
  const char  **cp;
1064
0
  int     n = 0, choice = -1;
1065
1066
0
  for (cp = oe->choices; *cp != NULL; cp++) {
1067
0
    if (strcmp(*cp, value) == 0)
1068
0
      choice = n;
1069
0
    n++;
1070
0
  }
1071
0
  if (choice == -1) {
1072
0
    xasprintf(cause, "unknown value: %s", value);
1073
0
    return (-1);
1074
0
  }
1075
0
  return (choice);
1076
0
}
1077
1078
static int
1079
options_from_string_choice(const struct options_table_entry *oe,
1080
    struct options *oo, const char *name, const char *value, char **cause)
1081
0
{
1082
0
  int choice = -1;
1083
1084
0
  if (value == NULL) {
1085
0
    choice = options_get_number(oo, name);
1086
0
    if (choice < 2)
1087
0
      choice = !choice;
1088
0
  } else {
1089
0
    choice = options_find_choice(oe, value, cause);
1090
0
    if (choice < 0)
1091
0
      return (-1);
1092
0
  }
1093
0
  options_set_number(oo, name, choice);
1094
0
  return (0);
1095
0
}
1096
1097
int
1098
options_from_string(struct options *oo, const struct options_table_entry *oe,
1099
    const char *name, const char *value, int append, char **cause)
1100
0
{
1101
0
  enum options_table_type  type;
1102
0
  long long    number;
1103
0
  const char    *errstr, *new;
1104
0
  char      *old;
1105
0
  key_code     key;
1106
0
  struct cmd_parse_result *pr;
1107
1108
0
  if (oe != NULL) {
1109
0
    if (value == NULL &&
1110
0
        oe->type != OPTIONS_TABLE_FLAG &&
1111
0
        oe->type != OPTIONS_TABLE_CHOICE) {
1112
0
      xasprintf(cause, "empty value");
1113
0
      return (-1);
1114
0
    }
1115
0
    type = oe->type;
1116
0
  } else {
1117
0
    if (*name != '@') {
1118
0
      xasprintf(cause, "bad option name");
1119
0
      return (-1);
1120
0
    }
1121
0
    type = OPTIONS_TABLE_STRING;
1122
0
  }
1123
1124
0
  switch (type) {
1125
0
  case OPTIONS_TABLE_STRING:
1126
0
    old = xstrdup(options_get_string(oo, name));
1127
0
    options_set_string(oo, name, append, "%s", value);
1128
1129
0
    new = options_get_string(oo, name);
1130
0
    if (options_from_string_check(oe, new, cause) != 0) {
1131
0
      options_set_string(oo, name, 0, "%s", old);
1132
0
      free(old);
1133
0
      return (-1);
1134
0
    }
1135
0
    free(old);
1136
0
    return (0);
1137
0
  case OPTIONS_TABLE_NUMBER:
1138
0
    number = strtonum(value, oe->minimum, oe->maximum, &errstr);
1139
0
    if (errstr != NULL) {
1140
0
      xasprintf(cause, "value is %s: %s", errstr, value);
1141
0
      return (-1);
1142
0
    }
1143
0
    options_set_number(oo, name, number);
1144
0
    return (0);
1145
0
  case OPTIONS_TABLE_KEY:
1146
0
    key = key_string_lookup_string(value);
1147
0
    if (key == KEYC_UNKNOWN) {
1148
0
      xasprintf(cause, "bad key: %s", value);
1149
0
      return (-1);
1150
0
    }
1151
0
    options_set_number(oo, name, key);
1152
0
    return (0);
1153
0
  case OPTIONS_TABLE_COLOUR:
1154
0
    if ((number = colour_fromstring(value)) == -1) {
1155
0
      xasprintf(cause, "bad colour: %s", value);
1156
0
      return (-1);
1157
0
    }
1158
0
    options_set_number(oo, name, number);
1159
0
    return (0);
1160
0
  case OPTIONS_TABLE_FLAG:
1161
0
    return (options_from_string_flag(oo, name, value, cause));
1162
0
  case OPTIONS_TABLE_CHOICE:
1163
0
    return (options_from_string_choice(oe, oo, name, value, cause));
1164
0
  case OPTIONS_TABLE_COMMAND:
1165
0
    pr = cmd_parse_from_string(value, NULL);
1166
0
    switch (pr->status) {
1167
0
    case CMD_PARSE_ERROR:
1168
0
      *cause = pr->error;
1169
0
      return (-1);
1170
0
    case CMD_PARSE_SUCCESS:
1171
0
      options_set_command(oo, name, pr->cmdlist);
1172
0
      return (0);
1173
0
    }
1174
0
    break;
1175
0
  }
1176
0
  return (-1);
1177
0
}
1178
1179
void
1180
options_push_changes(const char *name)
1181
0
{
1182
0
  struct client   *loop;
1183
0
  struct session    *s;
1184
0
  struct window   *w;
1185
0
  struct window_pane  *wp;
1186
1187
0
  log_debug("%s: %s", __func__, name);
1188
1189
0
  if (strcmp(name, "automatic-rename") == 0) {
1190
0
    RB_FOREACH(w, windows, &windows) {
1191
0
      if (w->active == NULL)
1192
0
        continue;
1193
0
      if (options_get_number(w->options, name))
1194
0
        w->active->flags |= PANE_CHANGED;
1195
0
    }
1196
0
  }
1197
0
  if (strcmp(name, "cursor-colour") == 0) {
1198
0
    RB_FOREACH(wp, window_pane_tree, &all_window_panes)
1199
0
      window_pane_default_cursor(wp);
1200
0
  }
1201
0
  if (strcmp(name, "cursor-style") == 0) {
1202
0
    RB_FOREACH(wp, window_pane_tree, &all_window_panes)
1203
0
      window_pane_default_cursor(wp);
1204
0
  }
1205
0
  if (strcmp(name, "fill-character") == 0) {
1206
0
    RB_FOREACH(w, windows, &windows)
1207
0
      window_set_fill_character(w);
1208
0
  }
1209
0
  if (strcmp(name, "key-table") == 0) {
1210
0
    TAILQ_FOREACH(loop, &clients, entry)
1211
0
      server_client_set_key_table(loop, NULL);
1212
0
  }
1213
0
  if (strcmp(name, "user-keys") == 0) {
1214
0
    TAILQ_FOREACH(loop, &clients, entry) {
1215
0
      if (loop->tty.flags & TTY_OPENED)
1216
0
        tty_keys_build(&loop->tty);
1217
0
    }
1218
0
  }
1219
0
  if (strcmp(name, "status") == 0 ||
1220
0
      strcmp(name, "status-interval") == 0)
1221
0
    status_timer_start_all();
1222
0
  if (strcmp(name, "monitor-silence") == 0)
1223
0
    alerts_reset_all();
1224
0
  if (strcmp(name, "window-style") == 0 ||
1225
0
      strcmp(name, "window-active-style") == 0) {
1226
0
    RB_FOREACH(wp, window_pane_tree, &all_window_panes)
1227
0
      wp->flags |= (PANE_STYLECHANGED|PANE_THEMECHANGED);
1228
0
  }
1229
0
  if (strcmp(name, "pane-colours") == 0) {
1230
0
    RB_FOREACH(wp, window_pane_tree, &all_window_panes)
1231
0
      colour_palette_from_option(&wp->palette, wp->options);
1232
0
  }
1233
0
  if (strcmp(name, "pane-border-status") == 0 ||
1234
0
      strcmp(name, "pane-scrollbars") == 0 ||
1235
0
      strcmp(name, "pane-scrollbars-position") == 0) {
1236
0
    RB_FOREACH(w, windows, &windows)
1237
0
      layout_fix_panes(w, NULL);
1238
0
  }
1239
0
  if (strcmp(name, "pane-scrollbars-style") == 0) {
1240
0
    RB_FOREACH(wp, window_pane_tree, &all_window_panes) {
1241
0
      style_set_scrollbar_style_from_option(
1242
0
          &wp->scrollbar_style, wp->options);
1243
0
    }
1244
0
    RB_FOREACH(w, windows, &windows)
1245
0
      layout_fix_panes(w, NULL);
1246
0
  }
1247
0
  if (strcmp(name, "codepoint-widths") == 0)
1248
0
    utf8_update_width_cache();
1249
0
  if (strcmp(name, "input-buffer-size") == 0)
1250
0
    input_set_buffer_size(options_get_number(global_options, name));
1251
0
  RB_FOREACH(s, sessions, &sessions)
1252
0
    status_update_cache(s);
1253
1254
0
  recalculate_sizes();
1255
0
  TAILQ_FOREACH(loop, &clients, entry) {
1256
0
    if (loop->session != NULL)
1257
0
      server_redraw_client(loop);
1258
0
  }
1259
0
}
1260
1261
int
1262
options_remove_or_default(struct options_entry *o, int idx, char **cause)
1263
100
{
1264
100
  struct options  *oo = o->owner;
1265
1266
100
  if (idx == -1) {
1267
100
    if (o->tableentry != NULL &&
1268
100
        (oo == global_options ||
1269
100
        oo == global_s_options ||
1270
100
        oo == global_w_options))
1271
0
      options_default(oo, o->tableentry);
1272
100
    else
1273
100
      options_remove(o);
1274
100
  } else if (options_array_set(o, idx, NULL, 0, cause) != 0)
1275
0
    return (-1);
1276
100
  return (0);
1277
100
}