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