options.c revision 1.49
1/* $OpenBSD: options.c,v 1.49 2019/06/20 07:41:29 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 33struct options_array_item { 34 u_int index; 35 union options_value value; 36 RB_ENTRY(options_array_item) entry; 37}; 38static int 39options_array_cmp(struct options_array_item *a1, struct options_array_item *a2) 40{ 41 if (a1->index < a2->index) 42 return (-1); 43 if (a1->index > a2->index) 44 return (1); 45 return (0); 46} 47RB_GENERATE_STATIC(options_array, options_array_item, entry, options_array_cmp); 48 49struct options_entry { 50 struct options *owner; 51 52 const char *name; 53 const struct options_table_entry *tableentry; 54 union options_value value; 55 56 RB_ENTRY(options_entry) entry; 57}; 58 59struct options { 60 RB_HEAD(options_tree, options_entry) tree; 61 struct options *parent; 62}; 63 64static struct options_entry *options_add(struct options *, const char *); 65 66#define OPTIONS_IS_STRING(o) \ 67 ((o)->tableentry == NULL || \ 68 (o)->tableentry->type == OPTIONS_TABLE_STRING) 69#define OPTIONS_IS_NUMBER(o) \ 70 ((o)->tableentry != NULL && \ 71 ((o)->tableentry->type == OPTIONS_TABLE_NUMBER || \ 72 (o)->tableentry->type == OPTIONS_TABLE_KEY || \ 73 (o)->tableentry->type == OPTIONS_TABLE_COLOUR || \ 74 (o)->tableentry->type == OPTIONS_TABLE_FLAG || \ 75 (o)->tableentry->type == OPTIONS_TABLE_CHOICE)) 76#define OPTIONS_IS_STYLE(o) \ 77 ((o)->tableentry != NULL && \ 78 (o)->tableentry->type == OPTIONS_TABLE_STYLE) 79#define OPTIONS_IS_COMMAND(o) \ 80 ((o)->tableentry != NULL && \ 81 (o)->tableentry->type == OPTIONS_TABLE_COMMAND) 82 83#define OPTIONS_IS_ARRAY(o) \ 84 ((o)->tableentry != NULL && \ 85 ((o)->tableentry->flags & OPTIONS_TABLE_IS_ARRAY)) 86 87static int options_cmp(struct options_entry *, struct options_entry *); 88RB_GENERATE_STATIC(options_tree, options_entry, entry, options_cmp); 89 90static int 91options_cmp(struct options_entry *lhs, struct options_entry *rhs) 92{ 93 return (strcmp(lhs->name, rhs->name)); 94} 95 96static const struct options_table_entry * 97options_parent_table_entry(struct options *oo, const char *s) 98{ 99 struct options_entry *o; 100 101 if (oo->parent == NULL) 102 fatalx("no parent options for %s", s); 103 o = options_get_only(oo->parent, s); 104 if (o == NULL) 105 fatalx("%s not in parent options", s); 106 return (o->tableentry); 107} 108 109static void 110options_value_free(struct options_entry *o, union options_value *ov) 111{ 112 if (OPTIONS_IS_STRING(o)) 113 free(ov->string); 114 if (OPTIONS_IS_COMMAND(o) && ov->cmdlist != NULL) 115 cmd_list_free(ov->cmdlist); 116} 117 118static char * 119options_value_tostring(struct options_entry *o, union options_value *ov, 120 int numeric) 121{ 122 char *s; 123 124 if (OPTIONS_IS_COMMAND(o)) 125 return (cmd_list_print(ov->cmdlist, 0)); 126 if (OPTIONS_IS_STYLE(o)) 127 return (xstrdup(style_tostring(&ov->style))); 128 if (OPTIONS_IS_NUMBER(o)) { 129 switch (o->tableentry->type) { 130 case OPTIONS_TABLE_NUMBER: 131 xasprintf(&s, "%lld", ov->number); 132 break; 133 case OPTIONS_TABLE_KEY: 134 s = xstrdup(key_string_lookup_key(ov->number)); 135 break; 136 case OPTIONS_TABLE_COLOUR: 137 s = xstrdup(colour_tostring(ov->number)); 138 break; 139 case OPTIONS_TABLE_FLAG: 140 if (numeric) 141 xasprintf(&s, "%lld", ov->number); 142 else 143 s = xstrdup(ov->number ? "on" : "off"); 144 break; 145 case OPTIONS_TABLE_CHOICE: 146 s = xstrdup(o->tableentry->choices[ov->number]); 147 break; 148 case OPTIONS_TABLE_STRING: 149 case OPTIONS_TABLE_STYLE: 150 case OPTIONS_TABLE_COMMAND: 151 fatalx("not a number option type"); 152 } 153 return (s); 154 } 155 if (OPTIONS_IS_STRING(o)) 156 return (xstrdup(ov->string)); 157 return (xstrdup("")); 158} 159 160struct options * 161options_create(struct options *parent) 162{ 163 struct options *oo; 164 165 oo = xcalloc(1, sizeof *oo); 166 RB_INIT(&oo->tree); 167 oo->parent = parent; 168 return (oo); 169} 170 171void 172options_free(struct options *oo) 173{ 174 struct options_entry *o, *tmp; 175 176 RB_FOREACH_SAFE(o, options_tree, &oo->tree, tmp) 177 options_remove(o); 178 free(oo); 179} 180 181struct options_entry * 182options_first(struct options *oo) 183{ 184 return (RB_MIN(options_tree, &oo->tree)); 185} 186 187struct options_entry * 188options_next(struct options_entry *o) 189{ 190 return (RB_NEXT(options_tree, &oo->tree, o)); 191} 192 193struct options_entry * 194options_get_only(struct options *oo, const char *name) 195{ 196 struct options_entry o; 197 198 o.name = name; 199 return (RB_FIND(options_tree, &oo->tree, &o)); 200} 201 202struct options_entry * 203options_get(struct options *oo, const char *name) 204{ 205 struct options_entry *o; 206 207 o = options_get_only(oo, name); 208 while (o == NULL) { 209 oo = oo->parent; 210 if (oo == NULL) 211 break; 212 o = options_get_only(oo, name); 213 } 214 return (o); 215} 216 217struct options_entry * 218options_empty(struct options *oo, const struct options_table_entry *oe) 219{ 220 struct options_entry *o; 221 222 o = options_add(oo, oe->name); 223 o->tableentry = oe; 224 225 if (oe->flags & OPTIONS_TABLE_IS_ARRAY) 226 RB_INIT(&o->value.array); 227 228 return (o); 229} 230 231struct options_entry * 232options_default(struct options *oo, const struct options_table_entry *oe) 233{ 234 struct options_entry *o; 235 union options_value *ov; 236 u_int i; 237 238 o = options_empty(oo, oe); 239 ov = &o->value; 240 241 if (oe->flags & OPTIONS_TABLE_IS_ARRAY) { 242 if (oe->default_arr == NULL) { 243 options_array_assign(o, oe->default_str, NULL); 244 return (o); 245 } 246 for (i = 0; oe->default_arr[i] != NULL; i++) 247 options_array_set(o, i, oe->default_arr[i], 0, NULL); 248 return (o); 249 } 250 251 switch (oe->type) { 252 case OPTIONS_TABLE_STRING: 253 ov->string = xstrdup(oe->default_str); 254 break; 255 case OPTIONS_TABLE_STYLE: 256 style_set(&ov->style, &grid_default_cell); 257 style_parse(&ov->style, &grid_default_cell, oe->default_str); 258 break; 259 default: 260 ov->number = oe->default_num; 261 break; 262 } 263 return (o); 264} 265 266static struct options_entry * 267options_add(struct options *oo, const char *name) 268{ 269 struct options_entry *o; 270 271 o = options_get_only(oo, name); 272 if (o != NULL) 273 options_remove(o); 274 275 o = xcalloc(1, sizeof *o); 276 o->owner = oo; 277 o->name = xstrdup(name); 278 279 RB_INSERT(options_tree, &oo->tree, o); 280 return (o); 281} 282 283void 284options_remove(struct options_entry *o) 285{ 286 struct options *oo = o->owner; 287 288 if (OPTIONS_IS_ARRAY(o)) 289 options_array_clear(o); 290 else 291 options_value_free(o, &o->value); 292 RB_REMOVE(options_tree, &oo->tree, o); 293 free(o); 294} 295 296const char * 297options_name(struct options_entry *o) 298{ 299 return (o->name); 300} 301 302const struct options_table_entry * 303options_table_entry(struct options_entry *o) 304{ 305 return (o->tableentry); 306} 307 308static struct options_array_item * 309options_array_item(struct options_entry *o, u_int idx) 310{ 311 struct options_array_item a; 312 313 a.index = idx; 314 return (RB_FIND(options_array, &o->value.array, &a)); 315} 316 317static void 318options_array_free(struct options_entry *o, struct options_array_item *a) 319{ 320 options_value_free(o, &a->value); 321 RB_REMOVE(options_array, &o->value.array, a); 322 free(a); 323} 324 325void 326options_array_clear(struct options_entry *o) 327{ 328 struct options_array_item *a, *a1; 329 330 if (!OPTIONS_IS_ARRAY(o)) 331 return; 332 333 RB_FOREACH_SAFE(a, options_array, &o->value.array, a1) 334 options_array_free(o, a); 335} 336 337union options_value * 338options_array_get(struct options_entry *o, u_int idx) 339{ 340 struct options_array_item *a; 341 342 if (!OPTIONS_IS_ARRAY(o)) 343 return (NULL); 344 a = options_array_item(o, idx); 345 if (a == NULL) 346 return (NULL); 347 return (&a->value); 348} 349 350int 351options_array_set(struct options_entry *o, u_int idx, const char *value, 352 int append, char **cause) 353{ 354 struct options_array_item *a; 355 char *new; 356 struct cmd_parse_result *pr; 357 358 if (!OPTIONS_IS_ARRAY(o)) { 359 if (cause != NULL) 360 *cause = xstrdup("not an array"); 361 return (-1); 362 } 363 364 if (OPTIONS_IS_COMMAND(o) && value != NULL) { 365 pr = cmd_parse_from_string(value, NULL); 366 switch (pr->status) { 367 case CMD_PARSE_EMPTY: 368 if (cause != NULL) 369 *cause = xstrdup("empty command"); 370 return (-1); 371 case CMD_PARSE_ERROR: 372 if (cause != NULL) 373 *cause = pr->error; 374 else 375 free(pr->error); 376 return (-1); 377 case CMD_PARSE_SUCCESS: 378 break; 379 } 380 } 381 382 a = options_array_item(o, idx); 383 if (value == NULL) { 384 if (a != NULL) 385 options_array_free(o, a); 386 return (0); 387 } 388 389 if (OPTIONS_IS_STRING(o)) { 390 if (a != NULL && append) 391 xasprintf(&new, "%s%s", a->value.string, value); 392 else 393 new = xstrdup(value); 394 } 395 396 if (a == NULL) { 397 a = xcalloc(1, sizeof *a); 398 a->index = idx; 399 RB_INSERT(options_array, &o->value.array, a); 400 } else 401 options_value_free(o, &a->value); 402 403 if (OPTIONS_IS_STRING(o)) 404 a->value.string = new; 405 else if (OPTIONS_IS_COMMAND(o)) 406 a->value.cmdlist = pr->cmdlist; 407 return (0); 408} 409 410int 411options_array_assign(struct options_entry *o, const char *s, char **cause) 412{ 413 const char *separator; 414 char *copy, *next, *string; 415 u_int i; 416 417 separator = o->tableentry->separator; 418 if (separator == NULL) 419 separator = " ,"; 420 if (*separator == '\0') { 421 if (*s == '\0') 422 return (0); 423 for (i = 0; i < UINT_MAX; i++) { 424 if (options_array_item(o, i) == NULL) 425 break; 426 } 427 return (options_array_set(o, i, s, 0, cause)); 428 } 429 430 if (*s == '\0') 431 return (0); 432 copy = string = xstrdup(s); 433 while ((next = strsep(&string, separator)) != NULL) { 434 if (*next == '\0') 435 continue; 436 for (i = 0; i < UINT_MAX; i++) { 437 if (options_array_item(o, i) == NULL) 438 break; 439 } 440 if (i == UINT_MAX) 441 break; 442 if (options_array_set(o, i, next, 0, cause) != 0) { 443 free(copy); 444 return (-1); 445 } 446 } 447 free(copy); 448 return (0); 449} 450 451struct options_array_item * 452options_array_first(struct options_entry *o) 453{ 454 if (!OPTIONS_IS_ARRAY(o)) 455 return (NULL); 456 return (RB_MIN(options_array, &o->value.array)); 457} 458 459struct options_array_item * 460options_array_next(struct options_array_item *a) 461{ 462 return (RB_NEXT(options_array, &o->value.array, a)); 463} 464 465u_int 466options_array_item_index(struct options_array_item *a) 467{ 468 return (a->index); 469} 470 471union options_value * 472options_array_item_value(struct options_array_item *a) 473{ 474 return (&a->value); 475} 476 477int 478options_isarray(struct options_entry *o) 479{ 480 return (OPTIONS_IS_ARRAY(o)); 481} 482 483int 484options_isstring(struct options_entry *o) 485{ 486 return (OPTIONS_IS_STRING(o)); 487} 488 489char * 490options_tostring(struct options_entry *o, int idx, int numeric) 491{ 492 struct options_array_item *a; 493 494 if (OPTIONS_IS_ARRAY(o)) { 495 if (idx == -1) 496 return (xstrdup("")); 497 a = options_array_item(o, idx); 498 if (a == NULL) 499 return (xstrdup("")); 500 return (options_value_tostring(o, &a->value, numeric)); 501 } 502 return (options_value_tostring(o, &o->value, numeric)); 503} 504 505char * 506options_parse(const char *name, int *idx) 507{ 508 char *copy, *cp, *end; 509 510 if (*name == '\0') 511 return (NULL); 512 copy = xstrdup(name); 513 if ((cp = strchr(copy, '[')) == NULL) { 514 *idx = -1; 515 return (copy); 516 } 517 end = strchr(cp + 1, ']'); 518 if (end == NULL || end[1] != '\0' || !isdigit((u_char)end[-1])) { 519 free(copy); 520 return (NULL); 521 } 522 if (sscanf(cp, "[%d]", idx) != 1 || *idx < 0) { 523 free(copy); 524 return (NULL); 525 } 526 *cp = '\0'; 527 return (copy); 528} 529 530struct options_entry * 531options_parse_get(struct options *oo, const char *s, int *idx, int only) 532{ 533 struct options_entry *o; 534 char *name; 535 536 name = options_parse(s, idx); 537 if (name == NULL) 538 return (NULL); 539 if (only) 540 o = options_get_only(oo, name); 541 else 542 o = options_get(oo, name); 543 free(name); 544 return (o); 545} 546 547char * 548options_match(const char *s, int *idx, int* ambiguous) 549{ 550 const struct options_table_entry *oe, *found; 551 char *name; 552 size_t namelen; 553 554 name = options_parse(s, idx); 555 if (name == NULL) 556 return (NULL); 557 namelen = strlen(name); 558 559 if (*name == '@') { 560 *ambiguous = 0; 561 return (name); 562 } 563 564 found = NULL; 565 for (oe = options_table; oe->name != NULL; oe++) { 566 if (strcmp(oe->name, name) == 0) { 567 found = oe; 568 break; 569 } 570 if (strncmp(oe->name, name, namelen) == 0) { 571 if (found != NULL) { 572 *ambiguous = 1; 573 free(name); 574 return (NULL); 575 } 576 found = oe; 577 } 578 } 579 free(name); 580 if (found == NULL) { 581 *ambiguous = 0; 582 return (NULL); 583 } 584 return (xstrdup(found->name)); 585} 586 587struct options_entry * 588options_match_get(struct options *oo, const char *s, int *idx, int only, 589 int* ambiguous) 590{ 591 char *name; 592 struct options_entry *o; 593 594 name = options_match(s, idx, ambiguous); 595 if (name == NULL) 596 return (NULL); 597 *ambiguous = 0; 598 if (only) 599 o = options_get_only(oo, name); 600 else 601 o = options_get(oo, name); 602 free(name); 603 return (o); 604} 605 606const char * 607options_get_string(struct options *oo, const char *name) 608{ 609 struct options_entry *o; 610 611 o = options_get(oo, name); 612 if (o == NULL) 613 fatalx("missing option %s", name); 614 if (!OPTIONS_IS_STRING(o)) 615 fatalx("option %s is not a string", name); 616 return (o->value.string); 617} 618 619long long 620options_get_number(struct options *oo, const char *name) 621{ 622 struct options_entry *o; 623 624 o = options_get(oo, name); 625 if (o == NULL) 626 fatalx("missing option %s", name); 627 if (!OPTIONS_IS_NUMBER(o)) 628 fatalx("option %s is not a number", name); 629 return (o->value.number); 630} 631 632struct style * 633options_get_style(struct options *oo, const char *name) 634{ 635 struct options_entry *o; 636 637 o = options_get(oo, name); 638 if (o == NULL) 639 fatalx("missing option %s", name); 640 if (!OPTIONS_IS_STYLE(o)) 641 fatalx("option %s is not a style", name); 642 return (&o->value.style); 643} 644 645struct options_entry * 646options_set_string(struct options *oo, const char *name, int append, 647 const char *fmt, ...) 648{ 649 struct options_entry *o; 650 va_list ap; 651 char *s, *value; 652 653 va_start(ap, fmt); 654 xvasprintf(&s, fmt, ap); 655 va_end(ap); 656 657 o = options_get_only(oo, name); 658 if (o != NULL && append && OPTIONS_IS_STRING(o)) { 659 xasprintf(&value, "%s%s", o->value.string, s); 660 free(s); 661 } else 662 value = s; 663 if (o == NULL && *name == '@') 664 o = options_add(oo, name); 665 else if (o == NULL) { 666 o = options_default(oo, options_parent_table_entry(oo, name)); 667 if (o == NULL) 668 return (NULL); 669 } 670 671 if (!OPTIONS_IS_STRING(o)) 672 fatalx("option %s is not a string", name); 673 free(o->value.string); 674 o->value.string = value; 675 return (o); 676} 677 678struct options_entry * 679options_set_number(struct options *oo, const char *name, long long value) 680{ 681 struct options_entry *o; 682 683 if (*name == '@') 684 fatalx("user option %s must be a string", name); 685 686 o = options_get_only(oo, name); 687 if (o == NULL) { 688 o = options_default(oo, options_parent_table_entry(oo, name)); 689 if (o == NULL) 690 return (NULL); 691 } 692 693 if (!OPTIONS_IS_NUMBER(o)) 694 fatalx("option %s is not a number", name); 695 o->value.number = value; 696 return (o); 697} 698 699struct options_entry * 700options_set_style(struct options *oo, const char *name, int append, 701 const char *value) 702{ 703 struct options_entry *o; 704 struct style sy; 705 706 if (*name == '@') 707 fatalx("user option %s must be a string", name); 708 709 o = options_get_only(oo, name); 710 if (o != NULL && append && OPTIONS_IS_STYLE(o)) 711 style_copy(&sy, &o->value.style); 712 else 713 style_set(&sy, &grid_default_cell); 714 if (style_parse(&sy, &grid_default_cell, value) == -1) 715 return (NULL); 716 if (o == NULL) { 717 o = options_default(oo, options_parent_table_entry(oo, name)); 718 if (o == NULL) 719 return (NULL); 720 } 721 722 if (!OPTIONS_IS_STYLE(o)) 723 fatalx("option %s is not a style", name); 724 style_copy(&o->value.style, &sy); 725 return (o); 726} 727 728enum options_table_scope 729options_scope_from_name(struct args *args, int window, 730 const char *name, struct cmd_find_state *fs, struct options **oo, 731 char **cause) 732{ 733 struct session *s = fs->s; 734 struct winlink *wl = fs->wl; 735 const char *target = args_get(args, 't'); 736 enum options_table_scope scope; 737 738 if (*name == '@') 739 return (options_scope_from_flags(args, window, fs, oo, cause)); 740 741 if (options_get_only(global_options, name) != NULL) 742 scope = OPTIONS_TABLE_SERVER; 743 else if (options_get_only(global_s_options, name) != NULL) 744 scope = OPTIONS_TABLE_SESSION; 745 else if (options_get_only(global_w_options, name) != NULL) 746 scope = OPTIONS_TABLE_WINDOW; 747 else { 748 xasprintf(cause, "unknown option: %s", name); 749 return (OPTIONS_TABLE_NONE); 750 } 751 752 if (scope == OPTIONS_TABLE_SERVER) 753 *oo = global_options; 754 else if (scope == OPTIONS_TABLE_SESSION) { 755 if (args_has(args, 'g')) 756 *oo = global_s_options; 757 else if (s == NULL) { 758 if (target != NULL) 759 xasprintf(cause, "no such session: %s", target); 760 else 761 xasprintf(cause, "no current session"); 762 } else 763 *oo = s->options; 764 } else if (scope == OPTIONS_TABLE_WINDOW) { 765 if (args_has(args, 'g')) 766 *oo = global_w_options; 767 else if (wl == NULL) { 768 if (target != NULL) 769 xasprintf(cause, "no such window: %s", target); 770 else 771 xasprintf(cause, "no current window"); 772 } else 773 *oo = wl->window->options; 774 } 775 return (scope); 776} 777 778enum options_table_scope 779options_scope_from_flags(struct args *args, int window, 780 struct cmd_find_state *fs, struct options **oo, char **cause) 781{ 782 struct session *s = fs->s; 783 struct winlink *wl = fs->wl; 784 const char *target = args_get(args, 't'); 785 786 if (args_has(args, 's')) { 787 *oo = global_options; 788 return (OPTIONS_TABLE_SERVER); 789 } 790 791 if (window || args_has(args, 'w')) { 792 if (args_has(args, 'g')) { 793 *oo = global_w_options; 794 return (OPTIONS_TABLE_WINDOW); 795 } 796 if (wl == NULL) { 797 if (target != NULL) 798 xasprintf(cause, "no such window: %s", target); 799 else 800 xasprintf(cause, "no current window"); 801 return (OPTIONS_TABLE_NONE); 802 } 803 *oo = wl->window->options; 804 return (OPTIONS_TABLE_WINDOW); 805 } else { 806 if (args_has(args, 'g')) { 807 *oo = global_s_options; 808 return (OPTIONS_TABLE_SESSION); 809 } 810 if (s == NULL) { 811 if (target != NULL) 812 xasprintf(cause, "no such session: %s", target); 813 else 814 xasprintf(cause, "no current session"); 815 return (OPTIONS_TABLE_NONE); 816 } 817 *oo = s->options; 818 return (OPTIONS_TABLE_SESSION); 819 } 820} 821