Annotation of src/usr.bin/tmux/options.c, Revision 1.26
1.26 ! nicm 1: /* $OpenBSD: options.c,v 1.25 2017/01/15 20:14:36 nicm Exp $ */
1.1 nicm 2:
3: /*
1.18 nicm 4: * Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
1.1 nicm 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:
1.26 ! nicm 21: #include <ctype.h>
1.1 nicm 22: #include <stdarg.h>
1.8 nicm 23: #include <stdlib.h>
1.1 nicm 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
1.9 nicm 30: * a red-black tree.
1.1 nicm 31: */
32:
1.26 ! nicm 33: struct option {
! 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(option) entry;
! 50: };
! 51:
1.13 nicm 52: struct options {
1.26 ! nicm 53: RB_HEAD(options_tree, option) tree;
! 54: struct options *parent;
1.13 nicm 55: };
56:
1.26 ! nicm 57: static struct option *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 option *, struct option *);
! 80: RB_GENERATE_STATIC(options_tree, option, entry, options_cmp);
1.1 nicm 81:
1.17 nicm 82: static int
1.26 ! nicm 83: options_cmp(struct option *lhs, struct option *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)
1.1 nicm 90: {
1.26 ! nicm 91: struct option *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);
1.1 nicm 99: }
100:
1.13 nicm 101: struct options *
102: options_create(struct options *parent)
1.1 nicm 103: {
1.16 nicm 104: struct options *oo;
1.13 nicm 105:
106: oo = xcalloc(1, sizeof *oo);
1.7 nicm 107: RB_INIT(&oo->tree);
1.1 nicm 108: oo->parent = parent;
1.13 nicm 109: return (oo);
1.1 nicm 110: }
111:
112: void
113: options_free(struct options *oo)
114: {
1.26 ! nicm 115: struct option *o, *tmp;
1.1 nicm 116:
1.26 ! nicm 117: RB_FOREACH_SAFE (o, options_tree, &oo->tree, tmp)
! 118: options_remove(o);
1.13 nicm 119: free(oo);
120: }
121:
1.26 ! nicm 122: struct option *
1.13 nicm 123: options_first(struct options *oo)
124: {
125: return (RB_MIN(options_tree, &oo->tree));
126: }
127:
1.26 ! nicm 128: struct option *
! 129: options_next(struct option *o)
1.13 nicm 130: {
131: return (RB_NEXT(options_tree, &oo->tree, o));
1.1 nicm 132: }
133:
1.26 ! nicm 134: struct option *
! 135: options_get_only(struct options *oo, const char *name)
1.1 nicm 136: {
1.26 ! nicm 137: struct option o;
1.1 nicm 138:
1.26 ! nicm 139: o.name = name;
! 140: return (RB_FIND(options_tree, &oo->tree, &o));
1.1 nicm 141: }
142:
1.26 ! nicm 143: struct option *
! 144: options_get(struct options *oo, const char *name)
1.1 nicm 145: {
1.26 ! nicm 146: struct option *o;
1.1 nicm 147:
1.26 ! nicm 148: o = options_get_only(oo, name);
1.1 nicm 149: while (o == NULL) {
150: oo = oo->parent;
151: if (oo == NULL)
152: break;
1.26 ! nicm 153: o = options_get_only(oo, name);
! 154: }
! 155: return (o);
! 156: }
! 157:
! 158: struct option *
! 159: options_empty(struct options *oo, const struct options_table_entry *oe)
! 160: {
! 161: struct option *o;
! 162:
! 163: o = options_add(oo, oe->name);
! 164: o->tableentry = oe;
! 165:
! 166: return (o);
! 167: }
! 168:
! 169: struct option *
! 170: options_default(struct options *oo, const struct options_table_entry *oe)
! 171: {
! 172: struct option *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);
1.1 nicm 186: }
1.26 ! nicm 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 option *
! 199: options_add(struct options *oo, const char *name)
! 200: {
! 201: struct option *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);
1.1 nicm 212: return (o);
213: }
214:
1.2 nicm 215: void
1.26 ! nicm 216: options_remove(struct option *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 option *o)
! 235: {
! 236: return (o->name);
! 237: }
! 238:
! 239: const struct options_table_entry *
! 240: options_table_entry(struct option *o)
! 241: {
! 242: return (o->tableentry);
! 243: }
! 244:
! 245: const char *
! 246: options_array_get(struct option *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 option *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 option *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 option *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 option *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)
1.1 nicm 350: {
1.26 ! nicm 351: char *copy, *cp, *end;
1.1 nicm 352:
1.26 ! nicm 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);
1.1 nicm 371: }
372:
1.26 ! nicm 373: struct option *
! 374: options_parse_get(struct options *oo, const char *s, int *idx, int only)
1.1 nicm 375: {
1.26 ! nicm 376: struct option *o;
! 377: char *name;
1.1 nicm 378:
1.26 ! nicm 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: }
1.1 nicm 395:
1.26 ! nicm 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: found = NULL;
! 407: for (oe = options_table; oe->name != NULL; oe++) {
! 408: if (strcmp(oe->name, name) == 0) {
! 409: found = oe;
! 410: break;
! 411: }
! 412: if (strncmp(oe->name, name, namelen) == 0) {
! 413: if (found != NULL) {
! 414: *ambiguous = 1;
! 415: free(name);
! 416: return (NULL);
! 417: }
! 418: found = oe;
! 419: }
! 420: }
! 421: free(name);
! 422: if (found == NULL) {
! 423: *ambiguous = 0;
! 424: return (NULL);
1.22 nicm 425: }
1.26 ! nicm 426: return (xstrdup(found->name));
! 427: }
1.22 nicm 428:
1.26 ! nicm 429: struct option *
! 430: options_match_get(struct options *oo, const char *s, int *idx, int only,
! 431: int* ambiguous)
! 432: {
! 433: char *name;
! 434: struct option *o;
1.21 nicm 435:
1.26 ! nicm 436: name = options_match(s, idx, ambiguous);
! 437: if (name == NULL)
! 438: return (NULL);
! 439: *ambiguous = 0;
! 440: if (only)
! 441: o = options_get_only(oo, name);
! 442: else
! 443: o = options_get(oo, name);
! 444: free(name);
! 445: if (o != NULL) {
! 446: if (OPTIONS_IS_ARRAY(o) && *idx == -1)
! 447: return (NULL);
! 448: if (!OPTIONS_IS_ARRAY(o) && *idx != -1)
! 449: return (NULL);
! 450: }
1.4 nicm 451: return (o);
1.1 nicm 452: }
453:
1.26 ! nicm 454:
1.23 nicm 455: const char *
1.1 nicm 456: options_get_string(struct options *oo, const char *name)
457: {
1.26 ! nicm 458: struct option *o;
! 459:
! 460: o = options_get(oo, name);
! 461: if (o == NULL)
! 462: fatalx("missing option %s", name);
! 463: if (!OPTIONS_IS_STRING(o))
! 464: fatalx("option %s is not a string", name);
! 465: return (o->string);
! 466: }
! 467:
! 468: long long
! 469: options_get_number(struct options *oo, const char *name)
! 470: {
! 471: struct option *o;
1.1 nicm 472:
1.26 ! nicm 473: o = options_get(oo, name);
! 474: if (o == NULL)
1.15 nicm 475: fatalx("missing option %s", name);
1.26 ! nicm 476: if (!OPTIONS_IS_NUMBER(o))
! 477: fatalx("option %s is not a number", name);
! 478: return (o->number);
1.1 nicm 479: }
480:
1.26 ! nicm 481: const struct grid_cell *
! 482: options_get_style(struct options *oo, const char *name)
! 483: {
! 484: struct option *o;
! 485:
! 486: o = options_get(oo, name);
! 487: if (o == NULL)
! 488: fatalx("missing option %s", name);
! 489: if (!OPTIONS_IS_STYLE(o))
! 490: fatalx("option %s is not a style", name);
! 491: return (&o->style);
! 492: }
! 493:
! 494: struct option *
! 495: options_set_string(struct options *oo, const char *name, int append,
! 496: const char *fmt, ...)
1.1 nicm 497: {
1.26 ! nicm 498: struct option *o;
! 499: va_list ap;
! 500: char *s, *value;
1.1 nicm 501:
1.26 ! nicm 502: va_start(ap, fmt);
! 503: xvasprintf(&s, fmt, ap);
! 504: va_end(ap);
! 505:
! 506: o = options_get_only(oo, name);
! 507: if (o != NULL && append && OPTIONS_IS_STRING(o)) {
! 508: xasprintf(&value, "%s%s", o->string, s);
! 509: free(s);
! 510: } else
! 511: value = s;
! 512: if (o == NULL && *name == '@')
! 513: o = options_add(oo, name);
! 514: else if (o == NULL) {
! 515: o = options_default(oo, options_parent_table_entry(oo, name));
! 516: if (o == NULL)
! 517: return (NULL);
! 518: }
1.21 nicm 519:
1.26 ! nicm 520: if (!OPTIONS_IS_STRING(o))
! 521: fatalx("option %s is not a string", name);
! 522: free(o->string);
! 523: o->string = value;
1.4 nicm 524: return (o);
1.1 nicm 525: }
526:
1.26 ! nicm 527: struct option *
! 528: options_set_number(struct options *oo, const char *name, long long value)
1.1 nicm 529: {
1.26 ! nicm 530: struct option *o;
! 531:
! 532: if (*name == '@')
! 533: fatalx("user option %s must be a string", name);
1.1 nicm 534:
1.26 ! nicm 535: o = options_get_only(oo, name);
! 536: if (o == NULL) {
! 537: o = options_default(oo, options_parent_table_entry(oo, name));
! 538: if (o == NULL)
! 539: return (NULL);
! 540: }
! 541:
! 542: if (!OPTIONS_IS_NUMBER(o))
! 543: fatalx("option %s is not a number", name);
! 544: o->number = value;
! 545: return (o);
1.10 nicm 546: }
547:
1.26 ! nicm 548: struct option *
1.22 nicm 549: options_set_style(struct options *oo, const char *name, int append,
550: const char *value)
1.10 nicm 551: {
1.26 ! nicm 552: struct option *o;
! 553: struct grid_cell gc;
! 554:
! 555: if (*name == '@')
! 556: fatalx("user option %s must be a string", name);
1.10 nicm 557:
1.26 ! nicm 558: o = options_get_only(oo, name);
! 559: if (o != NULL && append && OPTIONS_IS_STYLE(o))
! 560: memcpy(&gc, &o->style, sizeof gc);
1.12 nicm 561: else
1.26 ! nicm 562: memcpy(&gc, &grid_default_cell, sizeof gc);
! 563: if (style_parse(&grid_default_cell, &gc, value) == -1)
! 564: return (NULL);
! 565: if (o == NULL) {
! 566: o = options_default(oo, options_parent_table_entry(oo, name));
! 567: if (o == NULL)
! 568: return (NULL);
! 569: }
! 570:
! 571: if (!OPTIONS_IS_STYLE(o))
! 572: fatalx("option %s is not a style", name);
! 573: memcpy(&o->style, &gc, sizeof o->style);
! 574: return (o);
! 575: }
1.12 nicm 576:
1.26 ! nicm 577: enum options_table_scope
! 578: options_scope_from_flags(struct args *args, int window,
! 579: struct cmd_find_state *fs, struct options **oo, char **cause)
! 580: {
! 581: struct session *s = fs->s;
! 582: struct winlink *wl = fs->wl;
! 583: const char *target= args_get(args, 't');
! 584:
! 585: if (args_has(args, 's')) {
! 586: *oo = global_options;
! 587: return (OPTIONS_TABLE_SERVER);
! 588: }
1.12 nicm 589:
1.26 ! nicm 590: if (window || args_has(args, 'w')) {
! 591: if (args_has(args, 'g')) {
! 592: *oo = global_w_options;
! 593: return (OPTIONS_TABLE_WINDOW);
! 594: }
! 595: if (wl == NULL) {
! 596: if (target != NULL)
! 597: xasprintf(cause, "no such window: %s", target);
! 598: else
! 599: xasprintf(cause, "no current window");
! 600: return (OPTIONS_TABLE_NONE);
! 601: }
! 602: *oo = wl->window->options;
! 603: return (OPTIONS_TABLE_WINDOW);
! 604: } else {
! 605: if (args_has(args, 'g')) {
! 606: *oo = global_s_options;
! 607: return (OPTIONS_TABLE_SESSION);
! 608: }
! 609: if (s == NULL) {
! 610: if (target != NULL)
! 611: xasprintf(cause, "no such session: %s", target);
! 612: else
! 613: xasprintf(cause, "no current session");
! 614: return (OPTIONS_TABLE_NONE);
! 615: }
! 616: *oo = s->options;
! 617: return (OPTIONS_TABLE_SESSION);
! 618: }
! 619: }
1.21 nicm 620:
1.26 ! nicm 621: void
! 622: options_style_update_new(struct options *oo, struct option *o)
! 623: {
! 624: const char *newname = o->tableentry->style;
! 625: struct option *new;
! 626:
! 627: if (newname == NULL)
! 628: return;
! 629: new = options_get_only(oo, newname);
! 630: if (new == NULL)
! 631: new = options_set_style(oo, newname, 0, "default");
! 632:
! 633: if (strstr(o->name, "-bg") != NULL)
! 634: new->style.bg = o->number;
! 635: else if (strstr(o->name, "-fg") != NULL)
! 636: new->style.fg = o->number;
! 637: else if (strstr(o->name, "-attr") != NULL)
! 638: new->style.attr = o->number;
1.10 nicm 639: }
640:
1.26 ! nicm 641: void
! 642: options_style_update_old(struct options *oo, struct option *o)
1.10 nicm 643: {
1.26 ! nicm 644: char newname[128];
! 645: int size;
1.10 nicm 646:
1.26 ! nicm 647: size = strrchr(o->name, '-') - o->name;
! 648:
! 649: xsnprintf(newname, sizeof newname, "%.*s-bg", size, o->name);
! 650: options_set_number(oo, newname, o->style.bg);
! 651:
! 652: xsnprintf(newname, sizeof newname, "%.*s-fg", size, o->name);
! 653: options_set_number(oo, newname, o->style.fg);
! 654:
! 655: xsnprintf(newname, sizeof newname, "%.*s-attr", size, o->name);
! 656: options_set_number(oo, newname, o->style.attr);
1.1 nicm 657: }