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