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