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