1 /* $OpenBSD: options.c,v 1.43 2019/04/26 11:38:51 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)); 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_list *cmdlist; 357 358 if (!OPTIONS_IS_ARRAY(o)) { 359 *cause = xstrdup("not an array"); 360 return (-1); 361 } 362 363 if (OPTIONS_IS_COMMAND(o)) { 364 cmdlist = cmd_string_parse(value, NULL, 0, cause); 365 if (cmdlist == NULL && *cause != NULL) 366 return (-1); 367 } 368 369 a = options_array_item(o, idx); 370 if (value == NULL) { 371 if (a != NULL) 372 options_array_free(o, a); 373 return (0); 374 } 375 376 if (OPTIONS_IS_STRING(o)) { 377 if (a != NULL && append) 378 xasprintf(&new, "%s%s", a->value.string, value); 379 else 380 new = xstrdup(value); 381 } 382 383 if (a == NULL) { 384 a = xcalloc(1, sizeof *a); 385 a->index = idx; 386 RB_INSERT(options_array, &o->value.array, a); 387 } else 388 options_value_free(o, &a->value); 389 390 if (OPTIONS_IS_STRING(o)) 391 a->value.string = new; 392 else if (OPTIONS_IS_COMMAND(o)) 393 a->value.cmdlist = cmdlist; 394 return (0); 395 } 396 397 int 398 options_array_assign(struct options_entry *o, const char *s, char **cause) 399 { 400 const char *separator; 401 char *copy, *next, *string; 402 u_int i; 403 404 separator = o->tableentry->separator; 405 if (separator == NULL) 406 separator = " ,"; 407 if (*separator == '\0') { 408 if (*s == '\0') 409 return (0); 410 for (i = 0; i < UINT_MAX; i++) { 411 if (options_array_item(o, i) == NULL) 412 break; 413 } 414 return (options_array_set(o, i, s, 0, cause)); 415 } 416 417 if (*s == '\0') 418 return (0); 419 copy = string = xstrdup(s); 420 while ((next = strsep(&string, separator)) != NULL) { 421 if (*next == '\0') 422 continue; 423 for (i = 0; i < UINT_MAX; i++) { 424 if (options_array_item(o, i) == NULL) 425 break; 426 } 427 if (i == UINT_MAX) 428 break; 429 if (options_array_set(o, i, next, 0, cause) != 0) { 430 free(copy); 431 return (-1); 432 } 433 } 434 free(copy); 435 return (0); 436 } 437 438 struct options_array_item * 439 options_array_first(struct options_entry *o) 440 { 441 if (!OPTIONS_IS_ARRAY(o)) 442 return (NULL); 443 return (RB_MIN(options_array, &o->value.array)); 444 } 445 446 struct options_array_item * 447 options_array_next(struct options_array_item *a) 448 { 449 return (RB_NEXT(options_array, &o->value.array, a)); 450 } 451 452 u_int 453 options_array_item_index(struct options_array_item *a) 454 { 455 return (a->index); 456 } 457 458 union options_value * 459 options_array_item_value(struct options_array_item *a) 460 { 461 return (&a->value); 462 } 463 464 int 465 options_isarray(struct options_entry *o) 466 { 467 return (OPTIONS_IS_ARRAY(o)); 468 } 469 470 int 471 options_isstring(struct options_entry *o) 472 { 473 return (OPTIONS_IS_STRING(o)); 474 } 475 476 char * 477 options_tostring(struct options_entry *o, int idx, int numeric) 478 { 479 struct options_array_item *a; 480 481 if (OPTIONS_IS_ARRAY(o)) { 482 if (idx == -1) 483 return (xstrdup("")); 484 a = options_array_item(o, idx); 485 if (a == NULL) 486 return (xstrdup("")); 487 return (options_value_tostring(o, &a->value, numeric)); 488 } 489 return (options_value_tostring(o, &o->value, numeric)); 490 } 491 492 char * 493 options_parse(const char *name, int *idx) 494 { 495 char *copy, *cp, *end; 496 497 if (*name == '\0') 498 return (NULL); 499 copy = xstrdup(name); 500 if ((cp = strchr(copy, '[')) == NULL) { 501 *idx = -1; 502 return (copy); 503 } 504 end = strchr(cp + 1, ']'); 505 if (end == NULL || end[1] != '\0' || !isdigit((u_char)end[-1])) { 506 free(copy); 507 return (NULL); 508 } 509 if (sscanf(cp, "[%d]", idx) != 1 || *idx < 0) { 510 free(copy); 511 return (NULL); 512 } 513 *cp = '\0'; 514 return (copy); 515 } 516 517 struct options_entry * 518 options_parse_get(struct options *oo, const char *s, int *idx, int only) 519 { 520 struct options_entry *o; 521 char *name; 522 523 name = options_parse(s, idx); 524 if (name == NULL) 525 return (NULL); 526 if (only) 527 o = options_get_only(oo, name); 528 else 529 o = options_get(oo, name); 530 free(name); 531 return (o); 532 } 533 534 char * 535 options_match(const char *s, int *idx, int* ambiguous) 536 { 537 const struct options_table_entry *oe, *found; 538 char *name; 539 size_t namelen; 540 541 name = options_parse(s, idx); 542 if (name == NULL) 543 return (NULL); 544 namelen = strlen(name); 545 546 if (*name == '@') { 547 *ambiguous = 0; 548 return (name); 549 } 550 551 found = NULL; 552 for (oe = options_table; oe->name != NULL; oe++) { 553 if (strcmp(oe->name, name) == 0) { 554 found = oe; 555 break; 556 } 557 if (strncmp(oe->name, name, namelen) == 0) { 558 if (found != NULL) { 559 *ambiguous = 1; 560 free(name); 561 return (NULL); 562 } 563 found = oe; 564 } 565 } 566 free(name); 567 if (found == NULL) { 568 *ambiguous = 0; 569 return (NULL); 570 } 571 return (xstrdup(found->name)); 572 } 573 574 struct options_entry * 575 options_match_get(struct options *oo, const char *s, int *idx, int only, 576 int* ambiguous) 577 { 578 char *name; 579 struct options_entry *o; 580 581 name = options_match(s, idx, ambiguous); 582 if (name == NULL) 583 return (NULL); 584 *ambiguous = 0; 585 if (only) 586 o = options_get_only(oo, name); 587 else 588 o = options_get(oo, name); 589 free(name); 590 return (o); 591 } 592 593 const char * 594 options_get_string(struct options *oo, const char *name) 595 { 596 struct options_entry *o; 597 598 o = options_get(oo, name); 599 if (o == NULL) 600 fatalx("missing option %s", name); 601 if (!OPTIONS_IS_STRING(o)) 602 fatalx("option %s is not a string", name); 603 return (o->value.string); 604 } 605 606 long long 607 options_get_number(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_NUMBER(o)) 615 fatalx("option %s is not a number", name); 616 return (o->value.number); 617 } 618 619 struct style * 620 options_get_style(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_STYLE(o)) 628 fatalx("option %s is not a style", name); 629 return (&o->value.style); 630 } 631 632 struct options_entry * 633 options_set_string(struct options *oo, const char *name, int append, 634 const char *fmt, ...) 635 { 636 struct options_entry *o; 637 va_list ap; 638 char *s, *value; 639 640 va_start(ap, fmt); 641 xvasprintf(&s, fmt, ap); 642 va_end(ap); 643 644 o = options_get_only(oo, name); 645 if (o != NULL && append && OPTIONS_IS_STRING(o)) { 646 xasprintf(&value, "%s%s", o->value.string, s); 647 free(s); 648 } else 649 value = s; 650 if (o == NULL && *name == '@') 651 o = options_add(oo, name); 652 else if (o == NULL) { 653 o = options_default(oo, options_parent_table_entry(oo, name)); 654 if (o == NULL) 655 return (NULL); 656 } 657 658 if (!OPTIONS_IS_STRING(o)) 659 fatalx("option %s is not a string", name); 660 free(o->value.string); 661 o->value.string = value; 662 return (o); 663 } 664 665 struct options_entry * 666 options_set_number(struct options *oo, const char *name, long long value) 667 { 668 struct options_entry *o; 669 670 if (*name == '@') 671 fatalx("user option %s must be a string", name); 672 673 o = options_get_only(oo, name); 674 if (o == NULL) { 675 o = options_default(oo, options_parent_table_entry(oo, name)); 676 if (o == NULL) 677 return (NULL); 678 } 679 680 if (!OPTIONS_IS_NUMBER(o)) 681 fatalx("option %s is not a number", name); 682 o->value.number = value; 683 return (o); 684 } 685 686 struct options_entry * 687 options_set_style(struct options *oo, const char *name, int append, 688 const char *value) 689 { 690 struct options_entry *o; 691 struct style sy; 692 693 if (*name == '@') 694 fatalx("user option %s must be a string", name); 695 696 o = options_get_only(oo, name); 697 if (o != NULL && append && OPTIONS_IS_STYLE(o)) 698 style_copy(&sy, &o->value.style); 699 else 700 style_set(&sy, &grid_default_cell); 701 if (style_parse(&sy, &grid_default_cell, value) == -1) 702 return (NULL); 703 if (o == NULL) { 704 o = options_default(oo, options_parent_table_entry(oo, name)); 705 if (o == NULL) 706 return (NULL); 707 } 708 709 if (!OPTIONS_IS_STYLE(o)) 710 fatalx("option %s is not a style", name); 711 style_copy(&o->value.style, &sy); 712 return (o); 713 } 714 715 enum options_table_scope 716 options_scope_from_flags(struct args *args, int window, 717 struct cmd_find_state *fs, struct options **oo, char **cause) 718 { 719 struct session *s = fs->s; 720 struct winlink *wl = fs->wl; 721 const char *target= args_get(args, 't'); 722 723 if (args_has(args, 's')) { 724 *oo = global_options; 725 return (OPTIONS_TABLE_SERVER); 726 } 727 728 if (window || args_has(args, 'w')) { 729 if (args_has(args, 'g')) { 730 *oo = global_w_options; 731 return (OPTIONS_TABLE_WINDOW); 732 } 733 if (wl == NULL) { 734 if (target != NULL) 735 xasprintf(cause, "no such window: %s", target); 736 else 737 xasprintf(cause, "no current window"); 738 return (OPTIONS_TABLE_NONE); 739 } 740 *oo = wl->window->options; 741 return (OPTIONS_TABLE_WINDOW); 742 } else { 743 if (args_has(args, 'g')) { 744 *oo = global_s_options; 745 return (OPTIONS_TABLE_SESSION); 746 } 747 if (s == NULL) { 748 if (target != NULL) 749 xasprintf(cause, "no such session: %s", target); 750 else 751 xasprintf(cause, "no current session"); 752 return (OPTIONS_TABLE_NONE); 753 } 754 *oo = s->options; 755 return (OPTIONS_TABLE_SESSION); 756 } 757 } 758