1 /* $OpenBSD: options.c,v 1.46 2019/05/23 14:03:44 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_array_item { 34 u_int index; 35 union options_value value; 36 RB_ENTRY(options_array_item) entry; 37 }; 38 static int 39 options_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 } 47 RB_GENERATE_STATIC(options_array, options_array_item, entry, options_array_cmp); 48 49 struct 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 59 struct options { 60 RB_HEAD(options_tree, options_entry) tree; 61 struct options *parent; 62 }; 63 64 static 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 87 static int options_cmp(struct options_entry *, struct options_entry *); 88 RB_GENERATE_STATIC(options_tree, options_entry, entry, options_cmp); 89 90 static int 91 options_cmp(struct options_entry *lhs, struct options_entry *rhs) 92 { 93 return (strcmp(lhs->name, rhs->name)); 94 } 95 96 static const struct options_table_entry * 97 options_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 109 static void 110 options_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 118 static char * 119 options_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 160 struct options * 161 options_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 171 void 172 options_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 181 struct options_entry * 182 options_first(struct options *oo) 183 { 184 return (RB_MIN(options_tree, &oo->tree)); 185 } 186 187 struct options_entry * 188 options_next(struct options_entry *o) 189 { 190 return (RB_NEXT(options_tree, &oo->tree, o)); 191 } 192 193 struct options_entry * 194 options_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 202 struct options_entry * 203 options_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 217 struct options_entry * 218 options_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 231 struct options_entry * 232 options_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 266 static struct options_entry * 267 options_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 283 void 284 options_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 296 const char * 297 options_name(struct options_entry *o) 298 { 299 return (o->name); 300 } 301 302 const struct options_table_entry * 303 options_table_entry(struct options_entry *o) 304 { 305 return (o->tableentry); 306 } 307 308 static struct options_array_item * 309 options_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 317 static void 318 options_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 325 void 326 options_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 337 union options_value * 338 options_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 350 int 351 options_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)) { 365 pr = cmd_parse_from_string(value, NULL); 366 switch (pr->status) { 367 case CMD_PARSE_EMPTY: 368 *cause = xstrdup("empty command"); 369 return (-1); 370 case CMD_PARSE_ERROR: 371 if (cause != NULL) 372 *cause = pr->error; 373 else 374 free(pr->error); 375 return (-1); 376 case CMD_PARSE_SUCCESS: 377 break; 378 } 379 } 380 381 a = options_array_item(o, idx); 382 if (value == NULL) { 383 if (a != NULL) 384 options_array_free(o, a); 385 return (0); 386 } 387 388 if (OPTIONS_IS_STRING(o)) { 389 if (a != NULL && append) 390 xasprintf(&new, "%s%s", a->value.string, value); 391 else 392 new = xstrdup(value); 393 } 394 395 if (a == NULL) { 396 a = xcalloc(1, sizeof *a); 397 a->index = idx; 398 RB_INSERT(options_array, &o->value.array, a); 399 } else 400 options_value_free(o, &a->value); 401 402 if (OPTIONS_IS_STRING(o)) 403 a->value.string = new; 404 else if (OPTIONS_IS_COMMAND(o)) 405 a->value.cmdlist = pr->cmdlist; 406 return (0); 407 } 408 409 int 410 options_array_assign(struct options_entry *o, const char *s, char **cause) 411 { 412 const char *separator; 413 char *copy, *next, *string; 414 u_int i; 415 416 separator = o->tableentry->separator; 417 if (separator == NULL) 418 separator = " ,"; 419 if (*separator == '\0') { 420 if (*s == '\0') 421 return (0); 422 for (i = 0; i < UINT_MAX; i++) { 423 if (options_array_item(o, i) == NULL) 424 break; 425 } 426 return (options_array_set(o, i, s, 0, cause)); 427 } 428 429 if (*s == '\0') 430 return (0); 431 copy = string = xstrdup(s); 432 while ((next = strsep(&string, separator)) != NULL) { 433 if (*next == '\0') 434 continue; 435 for (i = 0; i < UINT_MAX; i++) { 436 if (options_array_item(o, i) == NULL) 437 break; 438 } 439 if (i == UINT_MAX) 440 break; 441 if (options_array_set(o, i, next, 0, cause) != 0) { 442 free(copy); 443 return (-1); 444 } 445 } 446 free(copy); 447 return (0); 448 } 449 450 struct options_array_item * 451 options_array_first(struct options_entry *o) 452 { 453 if (!OPTIONS_IS_ARRAY(o)) 454 return (NULL); 455 return (RB_MIN(options_array, &o->value.array)); 456 } 457 458 struct options_array_item * 459 options_array_next(struct options_array_item *a) 460 { 461 return (RB_NEXT(options_array, &o->value.array, a)); 462 } 463 464 u_int 465 options_array_item_index(struct options_array_item *a) 466 { 467 return (a->index); 468 } 469 470 union options_value * 471 options_array_item_value(struct options_array_item *a) 472 { 473 return (&a->value); 474 } 475 476 int 477 options_isarray(struct options_entry *o) 478 { 479 return (OPTIONS_IS_ARRAY(o)); 480 } 481 482 int 483 options_isstring(struct options_entry *o) 484 { 485 return (OPTIONS_IS_STRING(o)); 486 } 487 488 char * 489 options_tostring(struct options_entry *o, int idx, int numeric) 490 { 491 struct options_array_item *a; 492 493 if (OPTIONS_IS_ARRAY(o)) { 494 if (idx == -1) 495 return (xstrdup("")); 496 a = options_array_item(o, idx); 497 if (a == NULL) 498 return (xstrdup("")); 499 return (options_value_tostring(o, &a->value, numeric)); 500 } 501 return (options_value_tostring(o, &o->value, numeric)); 502 } 503 504 char * 505 options_parse(const char *name, int *idx) 506 { 507 char *copy, *cp, *end; 508 509 if (*name == '\0') 510 return (NULL); 511 copy = xstrdup(name); 512 if ((cp = strchr(copy, '[')) == NULL) { 513 *idx = -1; 514 return (copy); 515 } 516 end = strchr(cp + 1, ']'); 517 if (end == NULL || end[1] != '\0' || !isdigit((u_char)end[-1])) { 518 free(copy); 519 return (NULL); 520 } 521 if (sscanf(cp, "[%d]", idx) != 1 || *idx < 0) { 522 free(copy); 523 return (NULL); 524 } 525 *cp = '\0'; 526 return (copy); 527 } 528 529 struct options_entry * 530 options_parse_get(struct options *oo, const char *s, int *idx, int only) 531 { 532 struct options_entry *o; 533 char *name; 534 535 name = options_parse(s, idx); 536 if (name == NULL) 537 return (NULL); 538 if (only) 539 o = options_get_only(oo, name); 540 else 541 o = options_get(oo, name); 542 free(name); 543 return (o); 544 } 545 546 char * 547 options_match(const char *s, int *idx, int* ambiguous) 548 { 549 const struct options_table_entry *oe, *found; 550 char *name; 551 size_t namelen; 552 553 name = options_parse(s, idx); 554 if (name == NULL) 555 return (NULL); 556 namelen = strlen(name); 557 558 if (*name == '@') { 559 *ambiguous = 0; 560 return (name); 561 } 562 563 found = NULL; 564 for (oe = options_table; oe->name != NULL; oe++) { 565 if (strcmp(oe->name, name) == 0) { 566 found = oe; 567 break; 568 } 569 if (strncmp(oe->name, name, namelen) == 0) { 570 if (found != NULL) { 571 *ambiguous = 1; 572 free(name); 573 return (NULL); 574 } 575 found = oe; 576 } 577 } 578 free(name); 579 if (found == NULL) { 580 *ambiguous = 0; 581 return (NULL); 582 } 583 return (xstrdup(found->name)); 584 } 585 586 struct options_entry * 587 options_match_get(struct options *oo, const char *s, int *idx, int only, 588 int* ambiguous) 589 { 590 char *name; 591 struct options_entry *o; 592 593 name = options_match(s, idx, ambiguous); 594 if (name == NULL) 595 return (NULL); 596 *ambiguous = 0; 597 if (only) 598 o = options_get_only(oo, name); 599 else 600 o = options_get(oo, name); 601 free(name); 602 return (o); 603 } 604 605 const char * 606 options_get_string(struct options *oo, const char *name) 607 { 608 struct options_entry *o; 609 610 o = options_get(oo, name); 611 if (o == NULL) 612 fatalx("missing option %s", name); 613 if (!OPTIONS_IS_STRING(o)) 614 fatalx("option %s is not a string", name); 615 return (o->value.string); 616 } 617 618 long long 619 options_get_number(struct options *oo, const char *name) 620 { 621 struct options_entry *o; 622 623 o = options_get(oo, name); 624 if (o == NULL) 625 fatalx("missing option %s", name); 626 if (!OPTIONS_IS_NUMBER(o)) 627 fatalx("option %s is not a number", name); 628 return (o->value.number); 629 } 630 631 struct style * 632 options_get_style(struct options *oo, const char *name) 633 { 634 struct options_entry *o; 635 636 o = options_get(oo, name); 637 if (o == NULL) 638 fatalx("missing option %s", name); 639 if (!OPTIONS_IS_STYLE(o)) 640 fatalx("option %s is not a style", name); 641 return (&o->value.style); 642 } 643 644 struct options_entry * 645 options_set_string(struct options *oo, const char *name, int append, 646 const char *fmt, ...) 647 { 648 struct options_entry *o; 649 va_list ap; 650 char *s, *value; 651 652 va_start(ap, fmt); 653 xvasprintf(&s, fmt, ap); 654 va_end(ap); 655 656 o = options_get_only(oo, name); 657 if (o != NULL && append && OPTIONS_IS_STRING(o)) { 658 xasprintf(&value, "%s%s", o->value.string, s); 659 free(s); 660 } else 661 value = s; 662 if (o == NULL && *name == '@') 663 o = options_add(oo, name); 664 else if (o == NULL) { 665 o = options_default(oo, options_parent_table_entry(oo, name)); 666 if (o == NULL) 667 return (NULL); 668 } 669 670 if (!OPTIONS_IS_STRING(o)) 671 fatalx("option %s is not a string", name); 672 free(o->value.string); 673 o->value.string = value; 674 return (o); 675 } 676 677 struct options_entry * 678 options_set_number(struct options *oo, const char *name, long long value) 679 { 680 struct options_entry *o; 681 682 if (*name == '@') 683 fatalx("user option %s must be a string", name); 684 685 o = options_get_only(oo, name); 686 if (o == NULL) { 687 o = options_default(oo, options_parent_table_entry(oo, name)); 688 if (o == NULL) 689 return (NULL); 690 } 691 692 if (!OPTIONS_IS_NUMBER(o)) 693 fatalx("option %s is not a number", name); 694 o->value.number = value; 695 return (o); 696 } 697 698 struct options_entry * 699 options_set_style(struct options *oo, const char *name, int append, 700 const char *value) 701 { 702 struct options_entry *o; 703 struct style sy; 704 705 if (*name == '@') 706 fatalx("user option %s must be a string", name); 707 708 o = options_get_only(oo, name); 709 if (o != NULL && append && OPTIONS_IS_STYLE(o)) 710 style_copy(&sy, &o->value.style); 711 else 712 style_set(&sy, &grid_default_cell); 713 if (style_parse(&sy, &grid_default_cell, value) == -1) 714 return (NULL); 715 if (o == NULL) { 716 o = options_default(oo, options_parent_table_entry(oo, name)); 717 if (o == NULL) 718 return (NULL); 719 } 720 721 if (!OPTIONS_IS_STYLE(o)) 722 fatalx("option %s is not a style", name); 723 style_copy(&o->value.style, &sy); 724 return (o); 725 } 726 727 enum options_table_scope 728 options_scope_from_flags(struct args *args, int window, 729 struct cmd_find_state *fs, struct options **oo, char **cause) 730 { 731 struct session *s = fs->s; 732 struct winlink *wl = fs->wl; 733 const char *target= args_get(args, 't'); 734 735 if (args_has(args, 's')) { 736 *oo = global_options; 737 return (OPTIONS_TABLE_SERVER); 738 } 739 740 if (window || args_has(args, 'w')) { 741 if (args_has(args, 'g')) { 742 *oo = global_w_options; 743 return (OPTIONS_TABLE_WINDOW); 744 } 745 if (wl == NULL) { 746 if (target != NULL) 747 xasprintf(cause, "no such window: %s", target); 748 else 749 xasprintf(cause, "no current window"); 750 return (OPTIONS_TABLE_NONE); 751 } 752 *oo = wl->window->options; 753 return (OPTIONS_TABLE_WINDOW); 754 } else { 755 if (args_has(args, 'g')) { 756 *oo = global_s_options; 757 return (OPTIONS_TABLE_SESSION); 758 } 759 if (s == NULL) { 760 if (target != NULL) 761 xasprintf(cause, "no such session: %s", target); 762 else 763 xasprintf(cause, "no current session"); 764 return (OPTIONS_TABLE_NONE); 765 } 766 *oo = s->options; 767 return (OPTIONS_TABLE_SESSION); 768 } 769 } 770