Annotation of embedaddon/tmux/options.c, revision 1.1
1.1 ! misho 1: /* $OpenBSD$ */
! 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 (xstrdup(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: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>