version 1.25, 2017/01/15 20:14:36 |
version 1.26, 2017/01/15 20:48:41 |
|
|
|
|
#include <sys/types.h> |
#include <sys/types.h> |
|
|
|
#include <ctype.h> |
#include <stdarg.h> |
#include <stdarg.h> |
#include <stdlib.h> |
#include <stdlib.h> |
#include <string.h> |
#include <string.h> |
|
|
* a red-black tree. |
* a red-black tree. |
*/ |
*/ |
|
|
|
struct option { |
|
struct options *owner; |
|
|
|
const char *name; |
|
const struct options_table_entry *tableentry; |
|
|
|
union { |
|
char *string; |
|
long long number; |
|
struct grid_cell style; |
|
struct { |
|
const char **array; |
|
u_int arraysize; |
|
}; |
|
}; |
|
|
|
RB_ENTRY(option) entry; |
|
}; |
|
|
struct options { |
struct options { |
RB_HEAD(options_tree, options_entry) tree; |
RB_HEAD(options_tree, option) tree; |
struct options *parent; |
struct options *parent; |
}; |
}; |
|
|
static int options_cmp(struct options_entry *, struct options_entry *); |
static struct option *options_add(struct options *, const char *); |
RB_GENERATE_STATIC(options_tree, options_entry, entry, options_cmp); |
|
|
|
|
#define OPTIONS_ARRAY_LIMIT 1000 |
|
|
|
#define OPTIONS_IS_STRING(o) \ |
|
((o)->tableentry == NULL || \ |
|
(o)->tableentry->type == OPTIONS_TABLE_STRING) |
|
#define OPTIONS_IS_NUMBER(o) \ |
|
((o)->tableentry != NULL && \ |
|
((o)->tableentry->type == OPTIONS_TABLE_NUMBER || \ |
|
(o)->tableentry->type == OPTIONS_TABLE_KEY || \ |
|
(o)->tableentry->type == OPTIONS_TABLE_COLOUR || \ |
|
(o)->tableentry->type == OPTIONS_TABLE_ATTRIBUTES || \ |
|
(o)->tableentry->type == OPTIONS_TABLE_FLAG || \ |
|
(o)->tableentry->type == OPTIONS_TABLE_CHOICE)) |
|
#define OPTIONS_IS_STYLE(o) \ |
|
((o)->tableentry != NULL && \ |
|
(o)->tableentry->type == OPTIONS_TABLE_STYLE) |
|
#define OPTIONS_IS_ARRAY(o) \ |
|
((o)->tableentry != NULL && \ |
|
(o)->tableentry->type == OPTIONS_TABLE_ARRAY) |
|
|
|
static int options_cmp(struct option *, struct option *); |
|
RB_GENERATE_STATIC(options_tree, option, entry, options_cmp); |
|
|
static int |
static int |
options_cmp(struct options_entry *o1, struct options_entry *o2) |
options_cmp(struct option *lhs, struct option *rhs) |
{ |
{ |
return (strcmp(o1->name, o2->name)); |
return (strcmp(lhs->name, rhs->name)); |
} |
} |
|
|
|
static const struct options_table_entry * |
|
options_parent_table_entry(struct options *oo, const char *s) |
|
{ |
|
struct option *o; |
|
|
|
if (oo->parent == NULL) |
|
fatalx("no parent options for %s", s); |
|
o = options_get_only(oo->parent, s); |
|
if (o == NULL) |
|
fatalx("%s not in parent options", s); |
|
return (o->tableentry); |
|
} |
|
|
struct options * |
struct options * |
options_create(struct options *parent) |
options_create(struct options *parent) |
{ |
{ |
|
|
return (oo); |
return (oo); |
} |
} |
|
|
static void |
|
options_free1(struct options *oo, struct options_entry *o) |
|
{ |
|
RB_REMOVE(options_tree, &oo->tree, o); |
|
free((char *)o->name); |
|
if (o->type == OPTIONS_STRING) |
|
free(o->str); |
|
free(o); |
|
} |
|
|
|
static struct options_entry * |
|
options_new(struct options *oo, const char *name) |
|
{ |
|
struct options_entry *o; |
|
|
|
if ((o = options_find1(oo, name)) == NULL) { |
|
o = xmalloc(sizeof *o); |
|
o->name = xstrdup(name); |
|
RB_INSERT(options_tree, &oo->tree, o); |
|
memcpy(&o->style, &grid_default_cell, sizeof o->style); |
|
} else if (o->type == OPTIONS_STRING) |
|
free(o->str); |
|
return (o); |
|
} |
|
|
|
void |
void |
options_free(struct options *oo) |
options_free(struct options *oo) |
{ |
{ |
struct options_entry *o, *o1; |
struct option *o, *tmp; |
|
|
RB_FOREACH_SAFE (o, options_tree, &oo->tree, o1) |
RB_FOREACH_SAFE (o, options_tree, &oo->tree, tmp) |
options_free1(oo, o); |
options_remove(o); |
free(oo); |
free(oo); |
} |
} |
|
|
struct options_entry * |
struct option * |
options_first(struct options *oo) |
options_first(struct options *oo) |
{ |
{ |
return (RB_MIN(options_tree, &oo->tree)); |
return (RB_MIN(options_tree, &oo->tree)); |
} |
} |
|
|
struct options_entry * |
struct option * |
options_next(struct options_entry *o) |
options_next(struct option *o) |
{ |
{ |
return (RB_NEXT(options_tree, &oo->tree, o)); |
return (RB_NEXT(options_tree, &oo->tree, o)); |
} |
} |
|
|
struct options_entry * |
struct option * |
options_find1(struct options *oo, const char *name) |
options_get_only(struct options *oo, const char *name) |
{ |
{ |
struct options_entry p; |
struct option o; |
|
|
p.name = (char *)name; |
o.name = name; |
return (RB_FIND(options_tree, &oo->tree, &p)); |
return (RB_FIND(options_tree, &oo->tree, &o)); |
} |
} |
|
|
struct options_entry * |
struct option * |
options_find(struct options *oo, const char *name) |
options_get(struct options *oo, const char *name) |
{ |
{ |
struct options_entry *o, p; |
struct option *o; |
|
|
p.name = (char *)name; |
o = options_get_only(oo, name); |
o = RB_FIND(options_tree, &oo->tree, &p); |
|
while (o == NULL) { |
while (o == NULL) { |
oo = oo->parent; |
oo = oo->parent; |
if (oo == NULL) |
if (oo == NULL) |
break; |
break; |
o = RB_FIND(options_tree, &oo->tree, &p); |
o = options_get_only(oo, name); |
} |
} |
return (o); |
return (o); |
} |
} |
|
|
void |
struct option * |
options_remove(struct options *oo, const char *name) |
options_empty(struct options *oo, const struct options_table_entry *oe) |
{ |
{ |
struct options_entry *o; |
struct option *o; |
|
|
if ((o = options_find1(oo, name)) != NULL) |
o = options_add(oo, oe->name); |
options_free1(oo, o); |
o->tableentry = oe; |
|
|
|
return (o); |
} |
} |
|
|
struct options_entry * |
struct option * |
options_set_string(struct options *oo, const char *name, int append, |
options_default(struct options *oo, const struct options_table_entry *oe) |
const char *fmt, ...) |
|
{ |
{ |
struct options_entry *o; |
struct option *o; |
va_list ap; |
char *cp, *copy, *next; |
char *s, *value; |
u_int idx = 0; |
|
|
va_start(ap, fmt); |
o = options_empty(oo, oe); |
xvasprintf(&s, fmt, ap); |
|
va_end(ap); |
|
|
|
o = options_find1(oo, name); |
if (oe->type == OPTIONS_TABLE_ARRAY) { |
if (o == NULL || !append) |
copy = cp = xstrdup(oe->default_str); |
value = s; |
while ((next = strsep(&cp, ",")) != NULL) { |
else { |
options_array_set(o, idx, next); |
xasprintf(&value, "%s%s", o->str, s); |
idx++; |
free(s); |
} |
|
free(copy); |
|
return (o); |
} |
} |
|
|
o = options_new(oo, name); |
if (oe->type == OPTIONS_TABLE_STRING) |
o->type = OPTIONS_STRING; |
o->string = xstrdup(oe->default_str); |
o->str = value; |
else if (oe->type == OPTIONS_TABLE_STYLE) { |
|
memcpy(&o->style, &grid_default_cell, sizeof o->style); |
|
style_parse(&grid_default_cell, &o->style, oe->default_str); |
|
} else |
|
o->number = oe->default_num; |
|
return (o); |
|
} |
|
|
|
static struct option * |
|
options_add(struct options *oo, const char *name) |
|
{ |
|
struct option *o; |
|
|
|
o = options_get_only(oo, name); |
|
if (o != NULL) |
|
options_remove(o); |
|
|
|
o = xcalloc(1, sizeof *o); |
|
o->owner = oo; |
|
o->name = xstrdup(name); |
|
|
|
RB_INSERT(options_tree, &oo->tree, o); |
return (o); |
return (o); |
} |
} |
|
|
|
void |
|
options_remove(struct option *o) |
|
{ |
|
struct options *oo = o->owner; |
|
u_int i; |
|
|
|
if (OPTIONS_IS_STRING(o)) |
|
free((void *)o->string); |
|
else if (OPTIONS_IS_ARRAY(o)) { |
|
for (i = 0; i < o->arraysize; i++) |
|
free((void *)o->array[i]); |
|
free(o->array); |
|
} |
|
|
|
RB_REMOVE(options_tree, &oo->tree, o); |
|
free(o); |
|
} |
|
|
const char * |
const char * |
options_get_string(struct options *oo, const char *name) |
options_name(struct option *o) |
{ |
{ |
struct options_entry *o; |
return (o->name); |
|
} |
|
|
if ((o = options_find(oo, name)) == NULL) |
const struct options_table_entry * |
fatalx("missing option %s", name); |
options_table_entry(struct option *o) |
if (o->type != OPTIONS_STRING) |
{ |
fatalx("option %s not a string", name); |
return (o->tableentry); |
return (o->str); |
|
} |
} |
|
|
struct options_entry * |
const char * |
options_set_number(struct options *oo, const char *name, long long value) |
options_array_get(struct option *o, u_int idx) |
{ |
{ |
struct options_entry *o; |
if (!OPTIONS_IS_ARRAY(o)) |
|
return (NULL); |
|
if (idx >= o->arraysize) |
|
return (NULL); |
|
return (o->array[idx]); |
|
} |
|
|
o = options_new(oo, name); |
int |
o->type = OPTIONS_NUMBER; |
options_array_set(struct option *o, u_int idx, const char *value) |
o->num = value; |
{ |
|
u_int i; |
|
|
return (o); |
if (!OPTIONS_IS_ARRAY(o)) |
|
return (-1); |
|
|
|
if (idx >= OPTIONS_ARRAY_LIMIT) |
|
return (-1); |
|
if (idx >= o->arraysize) { |
|
o->array = xreallocarray(o->array, idx + 1, sizeof *o->array); |
|
for (i = o->arraysize; i < idx + 1; i++) |
|
o->array[i] = NULL; |
|
o->arraysize = idx + 1; |
|
} |
|
if (o->array[idx] != NULL) |
|
free((void *)o->array[idx]); |
|
if (value != NULL) |
|
o->array[idx] = xstrdup(value); |
|
else |
|
o->array[idx] = NULL; |
|
return (0); |
} |
} |
|
|
long long |
int |
options_get_number(struct options *oo, const char *name) |
options_array_size(struct option *o, u_int *size) |
{ |
{ |
struct options_entry *o; |
if (!OPTIONS_IS_ARRAY(o)) |
|
return (-1); |
|
if (size != NULL) |
|
*size = o->arraysize; |
|
return (0); |
|
} |
|
|
if ((o = options_find(oo, name)) == NULL) |
int |
fatalx("missing option %s", name); |
options_isstring(struct option *o) |
if (o->type != OPTIONS_NUMBER) |
{ |
fatalx("option %s not a number", name); |
if (o->tableentry == NULL) |
return (o->num); |
return (1); |
|
return (OPTIONS_IS_STRING(o) || OPTIONS_IS_ARRAY(o)); |
} |
} |
|
|
struct options_entry * |
const char * |
options_set_style(struct options *oo, const char *name, int append, |
options_tostring(struct option *o, int idx) |
const char *value) |
|
{ |
{ |
struct options_entry *o; |
static char s[1024]; |
struct grid_cell tmpgc; |
const char *tmp; |
|
|
o = options_find1(oo, name); |
if (OPTIONS_IS_ARRAY(o)) { |
if (o == NULL || !append) |
if (idx == -1) |
memcpy(&tmpgc, &grid_default_cell, sizeof tmpgc); |
return (NULL); |
|
if ((u_int)idx >= o->arraysize || o->array[idx] == NULL) |
|
return (""); |
|
return (o->array[idx]); |
|
} |
|
if (OPTIONS_IS_STYLE(o)) |
|
return (style_tostring(&o->style)); |
|
if (OPTIONS_IS_NUMBER(o)) { |
|
tmp = NULL; |
|
switch (o->tableentry->type) { |
|
case OPTIONS_TABLE_NUMBER: |
|
xsnprintf(s, sizeof s, "%lld", o->number); |
|
break; |
|
case OPTIONS_TABLE_KEY: |
|
tmp = key_string_lookup_key(o->number); |
|
break; |
|
case OPTIONS_TABLE_COLOUR: |
|
tmp = colour_tostring(o->number); |
|
break; |
|
case OPTIONS_TABLE_ATTRIBUTES: |
|
tmp = attributes_tostring(o->number); |
|
break; |
|
case OPTIONS_TABLE_FLAG: |
|
tmp = (o->number ? "on" : "off"); |
|
break; |
|
case OPTIONS_TABLE_CHOICE: |
|
tmp = o->tableentry->choices[o->number]; |
|
break; |
|
case OPTIONS_TABLE_STRING: |
|
case OPTIONS_TABLE_STYLE: |
|
case OPTIONS_TABLE_ARRAY: |
|
break; |
|
} |
|
if (tmp != NULL) |
|
xsnprintf(s, sizeof s, "%s", tmp); |
|
return (s); |
|
} |
|
if (OPTIONS_IS_STRING(o)) |
|
return (o->string); |
|
return (NULL); |
|
} |
|
|
|
char * |
|
options_parse(const char *name, int *idx) |
|
{ |
|
char *copy, *cp, *end; |
|
|
|
if (*name == '\0') |
|
return (NULL); |
|
copy = xstrdup(name); |
|
if ((cp = strchr(copy, '[')) == NULL) { |
|
*idx = -1; |
|
return (copy); |
|
} |
|
end = strchr(cp + 1, ']'); |
|
if (end == NULL || end[1] != '\0' || !isdigit((u_char)end[-1])) { |
|
free(copy); |
|
return (NULL); |
|
} |
|
if (sscanf(cp, "[%d]", idx) != 1 || *idx < 0) { |
|
free(copy); |
|
return (NULL); |
|
} |
|
*cp = '\0'; |
|
return (copy); |
|
} |
|
|
|
struct option * |
|
options_parse_get(struct options *oo, const char *s, int *idx, int only) |
|
{ |
|
struct option *o; |
|
char *name; |
|
|
|
name = options_parse(s, idx); |
|
if (name == NULL) |
|
return (NULL); |
|
if (only) |
|
o = options_get_only(oo, name); |
else |
else |
memcpy(&tmpgc, &o->style, sizeof tmpgc); |
o = options_get(oo, name); |
|
free(name); |
|
if (o != NULL) { |
|
if (OPTIONS_IS_ARRAY(o) && *idx == -1) |
|
return (NULL); |
|
if (!OPTIONS_IS_ARRAY(o) && *idx != -1) |
|
return (NULL); |
|
} |
|
return (o); |
|
} |
|
|
if (style_parse(&grid_default_cell, &tmpgc, value) == -1) |
char * |
|
options_match(const char *s, int *idx, int* ambiguous) |
|
{ |
|
const struct options_table_entry *oe, *found; |
|
char *name; |
|
size_t namelen; |
|
|
|
name = options_parse(s, idx); |
|
namelen = strlen(name); |
|
|
|
found = NULL; |
|
for (oe = options_table; oe->name != NULL; oe++) { |
|
if (strcmp(oe->name, name) == 0) { |
|
found = oe; |
|
break; |
|
} |
|
if (strncmp(oe->name, name, namelen) == 0) { |
|
if (found != NULL) { |
|
*ambiguous = 1; |
|
free(name); |
|
return (NULL); |
|
} |
|
found = oe; |
|
} |
|
} |
|
free(name); |
|
if (found == NULL) { |
|
*ambiguous = 0; |
return (NULL); |
return (NULL); |
|
} |
|
return (xstrdup(found->name)); |
|
} |
|
|
o = options_new(oo, name); |
struct option * |
o->type = OPTIONS_STYLE; |
options_match_get(struct options *oo, const char *s, int *idx, int only, |
memcpy(&o->style, &tmpgc, sizeof o->style); |
int* ambiguous) |
|
{ |
|
char *name; |
|
struct option *o; |
|
|
|
name = options_match(s, idx, ambiguous); |
|
if (name == NULL) |
|
return (NULL); |
|
*ambiguous = 0; |
|
if (only) |
|
o = options_get_only(oo, name); |
|
else |
|
o = options_get(oo, name); |
|
free(name); |
|
if (o != NULL) { |
|
if (OPTIONS_IS_ARRAY(o) && *idx == -1) |
|
return (NULL); |
|
if (!OPTIONS_IS_ARRAY(o) && *idx != -1) |
|
return (NULL); |
|
} |
return (o); |
return (o); |
} |
} |
|
|
|
|
|
const char * |
|
options_get_string(struct options *oo, const char *name) |
|
{ |
|
struct option *o; |
|
|
|
o = options_get(oo, name); |
|
if (o == NULL) |
|
fatalx("missing option %s", name); |
|
if (!OPTIONS_IS_STRING(o)) |
|
fatalx("option %s is not a string", name); |
|
return (o->string); |
|
} |
|
|
|
long long |
|
options_get_number(struct options *oo, const char *name) |
|
{ |
|
struct option *o; |
|
|
|
o = options_get(oo, name); |
|
if (o == NULL) |
|
fatalx("missing option %s", name); |
|
if (!OPTIONS_IS_NUMBER(o)) |
|
fatalx("option %s is not a number", name); |
|
return (o->number); |
|
} |
|
|
const struct grid_cell * |
const struct grid_cell * |
options_get_style(struct options *oo, const char *name) |
options_get_style(struct options *oo, const char *name) |
{ |
{ |
struct options_entry *o; |
struct option *o; |
|
|
if ((o = options_find(oo, name)) == NULL) |
o = options_get(oo, name); |
|
if (o == NULL) |
fatalx("missing option %s", name); |
fatalx("missing option %s", name); |
if (o->type != OPTIONS_STYLE) |
if (!OPTIONS_IS_STYLE(o)) |
fatalx("option %s not a style", name); |
fatalx("option %s is not a style", name); |
return (&o->style); |
return (&o->style); |
|
} |
|
|
|
struct option * |
|
options_set_string(struct options *oo, const char *name, int append, |
|
const char *fmt, ...) |
|
{ |
|
struct option *o; |
|
va_list ap; |
|
char *s, *value; |
|
|
|
va_start(ap, fmt); |
|
xvasprintf(&s, fmt, ap); |
|
va_end(ap); |
|
|
|
o = options_get_only(oo, name); |
|
if (o != NULL && append && OPTIONS_IS_STRING(o)) { |
|
xasprintf(&value, "%s%s", o->string, s); |
|
free(s); |
|
} else |
|
value = s; |
|
if (o == NULL && *name == '@') |
|
o = options_add(oo, name); |
|
else if (o == NULL) { |
|
o = options_default(oo, options_parent_table_entry(oo, name)); |
|
if (o == NULL) |
|
return (NULL); |
|
} |
|
|
|
if (!OPTIONS_IS_STRING(o)) |
|
fatalx("option %s is not a string", name); |
|
free(o->string); |
|
o->string = value; |
|
return (o); |
|
} |
|
|
|
struct option * |
|
options_set_number(struct options *oo, const char *name, long long value) |
|
{ |
|
struct option *o; |
|
|
|
if (*name == '@') |
|
fatalx("user option %s must be a string", name); |
|
|
|
o = options_get_only(oo, name); |
|
if (o == NULL) { |
|
o = options_default(oo, options_parent_table_entry(oo, name)); |
|
if (o == NULL) |
|
return (NULL); |
|
} |
|
|
|
if (!OPTIONS_IS_NUMBER(o)) |
|
fatalx("option %s is not a number", name); |
|
o->number = value; |
|
return (o); |
|
} |
|
|
|
struct option * |
|
options_set_style(struct options *oo, const char *name, int append, |
|
const char *value) |
|
{ |
|
struct option *o; |
|
struct grid_cell gc; |
|
|
|
if (*name == '@') |
|
fatalx("user option %s must be a string", name); |
|
|
|
o = options_get_only(oo, name); |
|
if (o != NULL && append && OPTIONS_IS_STYLE(o)) |
|
memcpy(&gc, &o->style, sizeof gc); |
|
else |
|
memcpy(&gc, &grid_default_cell, sizeof gc); |
|
if (style_parse(&grid_default_cell, &gc, value) == -1) |
|
return (NULL); |
|
if (o == NULL) { |
|
o = options_default(oo, options_parent_table_entry(oo, name)); |
|
if (o == NULL) |
|
return (NULL); |
|
} |
|
|
|
if (!OPTIONS_IS_STYLE(o)) |
|
fatalx("option %s is not a style", name); |
|
memcpy(&o->style, &gc, sizeof o->style); |
|
return (o); |
|
} |
|
|
|
enum options_table_scope |
|
options_scope_from_flags(struct args *args, int window, |
|
struct cmd_find_state *fs, struct options **oo, char **cause) |
|
{ |
|
struct session *s = fs->s; |
|
struct winlink *wl = fs->wl; |
|
const char *target= args_get(args, 't'); |
|
|
|
if (args_has(args, 's')) { |
|
*oo = global_options; |
|
return (OPTIONS_TABLE_SERVER); |
|
} |
|
|
|
if (window || args_has(args, 'w')) { |
|
if (args_has(args, 'g')) { |
|
*oo = global_w_options; |
|
return (OPTIONS_TABLE_WINDOW); |
|
} |
|
if (wl == NULL) { |
|
if (target != NULL) |
|
xasprintf(cause, "no such window: %s", target); |
|
else |
|
xasprintf(cause, "no current window"); |
|
return (OPTIONS_TABLE_NONE); |
|
} |
|
*oo = wl->window->options; |
|
return (OPTIONS_TABLE_WINDOW); |
|
} else { |
|
if (args_has(args, 'g')) { |
|
*oo = global_s_options; |
|
return (OPTIONS_TABLE_SESSION); |
|
} |
|
if (s == NULL) { |
|
if (target != NULL) |
|
xasprintf(cause, "no such session: %s", target); |
|
else |
|
xasprintf(cause, "no current session"); |
|
return (OPTIONS_TABLE_NONE); |
|
} |
|
*oo = s->options; |
|
return (OPTIONS_TABLE_SESSION); |
|
} |
|
} |
|
|
|
void |
|
options_style_update_new(struct options *oo, struct option *o) |
|
{ |
|
const char *newname = o->tableentry->style; |
|
struct option *new; |
|
|
|
if (newname == NULL) |
|
return; |
|
new = options_get_only(oo, newname); |
|
if (new == NULL) |
|
new = options_set_style(oo, newname, 0, "default"); |
|
|
|
if (strstr(o->name, "-bg") != NULL) |
|
new->style.bg = o->number; |
|
else if (strstr(o->name, "-fg") != NULL) |
|
new->style.fg = o->number; |
|
else if (strstr(o->name, "-attr") != NULL) |
|
new->style.attr = o->number; |
|
} |
|
|
|
void |
|
options_style_update_old(struct options *oo, struct option *o) |
|
{ |
|
char newname[128]; |
|
int size; |
|
|
|
size = strrchr(o->name, '-') - o->name; |
|
|
|
xsnprintf(newname, sizeof newname, "%.*s-bg", size, o->name); |
|
options_set_number(oo, newname, o->style.bg); |
|
|
|
xsnprintf(newname, sizeof newname, "%.*s-fg", size, o->name); |
|
options_set_number(oo, newname, o->style.fg); |
|
|
|
xsnprintf(newname, sizeof newname, "%.*s-attr", size, o->name); |
|
options_set_number(oo, newname, o->style.attr); |
} |
} |