1 /* $OpenBSD: options.c,v 1.30 2017/01/18 08:43:21 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 char *cp, *copy, *next; 174 u_int idx = 0; 175 176 o = options_empty(oo, oe); 177 178 if (oe->type == OPTIONS_TABLE_ARRAY) { 179 copy = cp = xstrdup(oe->default_str); 180 while ((next = strsep(&cp, ",")) != NULL) { 181 options_array_set(o, idx, next); 182 idx++; 183 } 184 free(copy); 185 return (o); 186 } 187 188 if (oe->type == OPTIONS_TABLE_STRING) 189 o->string = xstrdup(oe->default_str); 190 else if (oe->type == OPTIONS_TABLE_STYLE) { 191 memcpy(&o->style, &grid_default_cell, sizeof o->style); 192 style_parse(&grid_default_cell, &o->style, oe->default_str); 193 } else 194 o->number = oe->default_num; 195 return (o); 196 } 197 198 static struct options_entry * 199 options_add(struct options *oo, const char *name) 200 { 201 struct options_entry *o; 202 203 o = options_get_only(oo, name); 204 if (o != NULL) 205 options_remove(o); 206 207 o = xcalloc(1, sizeof *o); 208 o->owner = oo; 209 o->name = xstrdup(name); 210 211 RB_INSERT(options_tree, &oo->tree, o); 212 return (o); 213 } 214 215 void 216 options_remove(struct options_entry *o) 217 { 218 struct options *oo = o->owner; 219 u_int i; 220 221 if (OPTIONS_IS_STRING(o)) 222 free((void *)o->string); 223 else if (OPTIONS_IS_ARRAY(o)) { 224 for (i = 0; i < o->arraysize; i++) 225 free((void *)o->array[i]); 226 free(o->array); 227 } 228 229 RB_REMOVE(options_tree, &oo->tree, o); 230 free(o); 231 } 232 233 const char * 234 options_name(struct options_entry *o) 235 { 236 return (o->name); 237 } 238 239 const struct options_table_entry * 240 options_table_entry(struct options_entry *o) 241 { 242 return (o->tableentry); 243 } 244 245 const char * 246 options_array_get(struct options_entry *o, u_int idx) 247 { 248 if (!OPTIONS_IS_ARRAY(o)) 249 return (NULL); 250 if (idx >= o->arraysize) 251 return (NULL); 252 return (o->array[idx]); 253 } 254 255 int 256 options_array_set(struct options_entry *o, u_int idx, const char *value) 257 { 258 u_int i; 259 260 if (!OPTIONS_IS_ARRAY(o)) 261 return (-1); 262 263 if (idx >= OPTIONS_ARRAY_LIMIT) 264 return (-1); 265 if (idx >= o->arraysize) { 266 o->array = xreallocarray(o->array, idx + 1, sizeof *o->array); 267 for (i = o->arraysize; i < idx + 1; i++) 268 o->array[i] = NULL; 269 o->arraysize = idx + 1; 270 } 271 if (o->array[idx] != NULL) 272 free((void *)o->array[idx]); 273 if (value != NULL) 274 o->array[idx] = xstrdup(value); 275 else 276 o->array[idx] = NULL; 277 return (0); 278 } 279 280 int 281 options_array_size(struct options_entry *o, u_int *size) 282 { 283 if (!OPTIONS_IS_ARRAY(o)) 284 return (-1); 285 if (size != NULL) 286 *size = o->arraysize; 287 return (0); 288 } 289 290 int 291 options_isstring(struct options_entry *o) 292 { 293 if (o->tableentry == NULL) 294 return (1); 295 return (OPTIONS_IS_STRING(o) || OPTIONS_IS_ARRAY(o)); 296 } 297 298 const char * 299 options_tostring(struct options_entry *o, int idx) 300 { 301 static char s[1024]; 302 const char *tmp; 303 304 if (OPTIONS_IS_ARRAY(o)) { 305 if (idx == -1) 306 return (NULL); 307 if ((u_int)idx >= o->arraysize || o->array[idx] == NULL) 308 return (""); 309 return (o->array[idx]); 310 } 311 if (OPTIONS_IS_STYLE(o)) 312 return (style_tostring(&o->style)); 313 if (OPTIONS_IS_NUMBER(o)) { 314 tmp = NULL; 315 switch (o->tableentry->type) { 316 case OPTIONS_TABLE_NUMBER: 317 xsnprintf(s, sizeof s, "%lld", o->number); 318 break; 319 case OPTIONS_TABLE_KEY: 320 tmp = key_string_lookup_key(o->number); 321 break; 322 case OPTIONS_TABLE_COLOUR: 323 tmp = colour_tostring(o->number); 324 break; 325 case OPTIONS_TABLE_ATTRIBUTES: 326 tmp = attributes_tostring(o->number); 327 break; 328 case OPTIONS_TABLE_FLAG: 329 tmp = (o->number ? "on" : "off"); 330 break; 331 case OPTIONS_TABLE_CHOICE: 332 tmp = o->tableentry->choices[o->number]; 333 break; 334 case OPTIONS_TABLE_STRING: 335 case OPTIONS_TABLE_STYLE: 336 case OPTIONS_TABLE_ARRAY: 337 break; 338 } 339 if (tmp != NULL) 340 xsnprintf(s, sizeof s, "%s", tmp); 341 return (s); 342 } 343 if (OPTIONS_IS_STRING(o)) 344 return (o->string); 345 return (NULL); 346 } 347 348 char * 349 options_parse(const char *name, int *idx) 350 { 351 char *copy, *cp, *end; 352 353 if (*name == '\0') 354 return (NULL); 355 copy = xstrdup(name); 356 if ((cp = strchr(copy, '[')) == NULL) { 357 *idx = -1; 358 return (copy); 359 } 360 end = strchr(cp + 1, ']'); 361 if (end == NULL || end[1] != '\0' || !isdigit((u_char)end[-1])) { 362 free(copy); 363 return (NULL); 364 } 365 if (sscanf(cp, "[%d]", idx) != 1 || *idx < 0) { 366 free(copy); 367 return (NULL); 368 } 369 *cp = '\0'; 370 return (copy); 371 } 372 373 struct options_entry * 374 options_parse_get(struct options *oo, const char *s, int *idx, int only) 375 { 376 struct options_entry *o; 377 char *name; 378 379 name = options_parse(s, idx); 380 if (name == NULL) 381 return (NULL); 382 if (only) 383 o = options_get_only(oo, name); 384 else 385 o = options_get(oo, name); 386 free(name); 387 if (o != NULL) { 388 if (OPTIONS_IS_ARRAY(o) && *idx == -1) 389 return (NULL); 390 if (!OPTIONS_IS_ARRAY(o) && *idx != -1) 391 return (NULL); 392 } 393 return (o); 394 } 395 396 char * 397 options_match(const char *s, int *idx, int* ambiguous) 398 { 399 const struct options_table_entry *oe, *found; 400 char *name; 401 size_t namelen; 402 403 name = options_parse(s, idx); 404 namelen = strlen(name); 405 406 if (*name == '@') { 407 *ambiguous = 0; 408 return (xstrdup(name)); 409 } 410 411 found = NULL; 412 for (oe = options_table; oe->name != NULL; oe++) { 413 if (strcmp(oe->name, name) == 0) { 414 found = oe; 415 break; 416 } 417 if (strncmp(oe->name, name, namelen) == 0) { 418 if (found != NULL) { 419 *ambiguous = 1; 420 free(name); 421 return (NULL); 422 } 423 found = oe; 424 } 425 } 426 free(name); 427 if (found == NULL) { 428 *ambiguous = 0; 429 return (NULL); 430 } 431 return (xstrdup(found->name)); 432 } 433 434 struct options_entry * 435 options_match_get(struct options *oo, const char *s, int *idx, int only, 436 int* ambiguous) 437 { 438 char *name; 439 struct options_entry *o; 440 441 name = options_match(s, idx, ambiguous); 442 if (name == NULL) 443 return (NULL); 444 *ambiguous = 0; 445 if (only) 446 o = options_get_only(oo, name); 447 else 448 o = options_get(oo, name); 449 free(name); 450 if (o != NULL) { 451 if (OPTIONS_IS_ARRAY(o) && *idx == -1) 452 return (NULL); 453 if (!OPTIONS_IS_ARRAY(o) && *idx != -1) 454 return (NULL); 455 } 456 return (o); 457 } 458 459 460 const char * 461 options_get_string(struct options *oo, const char *name) 462 { 463 struct options_entry *o; 464 465 o = options_get(oo, name); 466 if (o == NULL) 467 fatalx("missing option %s", name); 468 if (!OPTIONS_IS_STRING(o)) 469 fatalx("option %s is not a string", name); 470 return (o->string); 471 } 472 473 long long 474 options_get_number(struct options *oo, const char *name) 475 { 476 struct options_entry *o; 477 478 o = options_get(oo, name); 479 if (o == NULL) 480 fatalx("missing option %s", name); 481 if (!OPTIONS_IS_NUMBER(o)) 482 fatalx("option %s is not a number", name); 483 return (o->number); 484 } 485 486 const struct grid_cell * 487 options_get_style(struct options *oo, const char *name) 488 { 489 struct options_entry *o; 490 491 o = options_get(oo, name); 492 if (o == NULL) 493 fatalx("missing option %s", name); 494 if (!OPTIONS_IS_STYLE(o)) 495 fatalx("option %s is not a style", name); 496 return (&o->style); 497 } 498 499 struct options_entry * 500 options_set_string(struct options *oo, const char *name, int append, 501 const char *fmt, ...) 502 { 503 struct options_entry *o; 504 va_list ap; 505 char *s, *value; 506 507 va_start(ap, fmt); 508 xvasprintf(&s, fmt, ap); 509 va_end(ap); 510 511 o = options_get_only(oo, name); 512 if (o != NULL && append && OPTIONS_IS_STRING(o)) { 513 xasprintf(&value, "%s%s", o->string, s); 514 free(s); 515 } else 516 value = s; 517 if (o == NULL && *name == '@') 518 o = options_add(oo, name); 519 else if (o == NULL) { 520 o = options_default(oo, options_parent_table_entry(oo, name)); 521 if (o == NULL) 522 return (NULL); 523 } 524 525 if (!OPTIONS_IS_STRING(o)) 526 fatalx("option %s is not a string", name); 527 free(o->string); 528 o->string = value; 529 return (o); 530 } 531 532 struct options_entry * 533 options_set_number(struct options *oo, const char *name, long long value) 534 { 535 struct options_entry *o; 536 537 if (*name == '@') 538 fatalx("user option %s must be a string", name); 539 540 o = options_get_only(oo, name); 541 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_NUMBER(o)) 548 fatalx("option %s is not a number", name); 549 o->number = value; 550 return (o); 551 } 552 553 struct options_entry * 554 options_set_style(struct options *oo, const char *name, int append, 555 const char *value) 556 { 557 struct options_entry *o; 558 struct grid_cell gc; 559 560 if (*name == '@') 561 fatalx("user option %s must be a string", name); 562 563 o = options_get_only(oo, name); 564 if (o != NULL && append && OPTIONS_IS_STYLE(o)) 565 memcpy(&gc, &o->style, sizeof gc); 566 else 567 memcpy(&gc, &grid_default_cell, sizeof gc); 568 if (style_parse(&grid_default_cell, &gc, value) == -1) 569 return (NULL); 570 if (o == NULL) { 571 o = options_default(oo, options_parent_table_entry(oo, name)); 572 if (o == NULL) 573 return (NULL); 574 } 575 576 if (!OPTIONS_IS_STYLE(o)) 577 fatalx("option %s is not a style", name); 578 memcpy(&o->style, &gc, sizeof o->style); 579 return (o); 580 } 581 582 enum options_table_scope 583 options_scope_from_flags(struct args *args, int window, 584 struct cmd_find_state *fs, struct options **oo, char **cause) 585 { 586 struct session *s = fs->s; 587 struct winlink *wl = fs->wl; 588 const char *target= args_get(args, 't'); 589 590 if (args_has(args, 's')) { 591 *oo = global_options; 592 return (OPTIONS_TABLE_SERVER); 593 } 594 595 if (window || args_has(args, 'w')) { 596 if (args_has(args, 'g')) { 597 *oo = global_w_options; 598 return (OPTIONS_TABLE_WINDOW); 599 } 600 if (wl == NULL) { 601 if (target != NULL) 602 xasprintf(cause, "no such window: %s", target); 603 else 604 xasprintf(cause, "no current window"); 605 return (OPTIONS_TABLE_NONE); 606 } 607 *oo = wl->window->options; 608 return (OPTIONS_TABLE_WINDOW); 609 } else { 610 if (args_has(args, 'g')) { 611 *oo = global_s_options; 612 return (OPTIONS_TABLE_SESSION); 613 } 614 if (s == NULL) { 615 if (target != NULL) 616 xasprintf(cause, "no such session: %s", target); 617 else 618 xasprintf(cause, "no current session"); 619 return (OPTIONS_TABLE_NONE); 620 } 621 *oo = s->options; 622 return (OPTIONS_TABLE_SESSION); 623 } 624 } 625 626 void 627 options_style_update_new(struct options *oo, struct options_entry *o) 628 { 629 const char *newname = o->tableentry->style; 630 struct options_entry *new; 631 632 if (newname == NULL) 633 return; 634 new = options_get_only(oo, newname); 635 if (new == NULL) 636 new = options_set_style(oo, newname, 0, "default"); 637 638 if (strstr(o->name, "-bg") != NULL) 639 new->style.bg = o->number; 640 else if (strstr(o->name, "-fg") != NULL) 641 new->style.fg = o->number; 642 else if (strstr(o->name, "-attr") != NULL) 643 new->style.attr = o->number; 644 } 645 646 void 647 options_style_update_old(struct options *oo, struct options_entry *o) 648 { 649 char newname[128]; 650 int size; 651 652 size = strrchr(o->name, '-') - o->name; 653 654 xsnprintf(newname, sizeof newname, "%.*s-bg", size, o->name); 655 if (options_get(oo, newname) != NULL) 656 options_set_number(oo, newname, o->style.bg); 657 658 xsnprintf(newname, sizeof newname, "%.*s-fg", size, o->name); 659 if (options_get(oo, newname) != NULL) 660 options_set_number(oo, newname, o->style.fg); 661 662 xsnprintf(newname, sizeof newname, "%.*s-attr", size, o->name); 663 if (options_get(oo, newname) != NULL) 664 options_set_number(oo, newname, o->style.attr); 665 } 666