Make array options a sparse tree instead of an array of char * and

remove the size limit.
pull/1644/head
nicm 2019-03-18 11:58:40 +00:00
parent d2d43987d0
commit ce6be7afd4
9 changed files with 197 additions and 117 deletions

View File

@ -163,11 +163,9 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
parent = options_get(oo, name); parent = options_get(oo, name);
/* Check that array options and indexes match up. */ /* Check that array options and indexes match up. */
if (idx != -1) { if (idx != -1 && (*name == '@' || !options_isarray(parent))) {
if (*name == '@' || options_array_size(parent, NULL) == -1) { cmdq_error(item, "not an array: %s", argument);
cmdq_error(item, "not an array: %s", argument); goto fail;
goto fail;
}
} }
/* With -o, check this option is not already set. */ /* With -o, check this option is not already set. */
@ -209,7 +207,7 @@ cmd_set_option_exec(struct cmd *self, struct cmdq_item *item)
goto fail; goto fail;
} }
options_set_string(oo, name, append, "%s", value); options_set_string(oo, name, append, "%s", value);
} else if (idx == -1 && options_array_size(parent, NULL) == -1) { } else if (idx == -1 && !options_isarray(parent)) {
error = cmd_set_option_set(self, item, oo, parent, value); error = cmd_set_option_set(self, item, oo, parent, value);
if (error != 0) if (error != 0)
goto fail; goto fail;

View File

@ -89,20 +89,20 @@ static void
cmd_show_options_print(struct cmd *self, struct cmdq_item *item, cmd_show_options_print(struct cmd *self, struct cmdq_item *item,
struct options_entry *o, int idx) struct options_entry *o, int idx)
{ {
const char *name; struct options_array_item *a;
const char *value; const char *name, *value;
char *tmp, *escaped; char *tmp, *escaped;
u_int size, i;
if (idx != -1) { if (idx != -1) {
xasprintf(&tmp, "%s[%d]", options_name(o), idx); xasprintf(&tmp, "%s[%d]", options_name(o), idx);
name = tmp; name = tmp;
} else { } else {
if (options_array_size(o, &size) != -1) { if (options_isarray(o)) {
for (i = 0; i < size; i++) { a = options_array_first(o);
if (options_array_get(o, i) == NULL) while (a != NULL) {
continue; idx = options_array_item_index(a);
cmd_show_options_print(self, item, o, i); cmd_show_options_print(self, item, o, idx);
a = options_array_next(a);
} }
return; return;
} }
@ -165,9 +165,10 @@ static enum cmd_retval
cmd_show_options_all(struct cmd *self, struct cmdq_item *item, cmd_show_options_all(struct cmd *self, struct cmdq_item *item,
struct options *oo) struct options *oo)
{ {
struct options_entry *o; struct options_entry *o;
const struct options_table_entry *oe; const struct options_table_entry *oe;
u_int size, idx; struct options_array_item *a;
u_int idx;
o = options_first(oo); o = options_first(oo);
while (o != NULL) { while (o != NULL) {
@ -176,13 +177,14 @@ cmd_show_options_all(struct cmd *self, struct cmdq_item *item,
o = options_next(o); o = options_next(o);
continue; continue;
} }
if (options_array_size(o, &size) == -1) if (!options_isarray(o))
cmd_show_options_print(self, item, o, -1); cmd_show_options_print(self, item, o, -1);
else { else {
for (idx = 0; idx < size; idx++) { a = options_array_first(o);
if (options_array_get(o, idx) == NULL) while (a != NULL) {
continue; idx = options_array_item_index(a);
cmd_show_options_print(self, item, o, idx); cmd_show_options_print(self, item, o, idx);
a = options_array_next(a);
} }
} }
o = options_next(o); o = options_next(o);

38
cmd.c
View File

@ -319,31 +319,31 @@ cmd_stringify_argv(int argc, char **argv)
static int static int
cmd_try_alias(int *argc, char ***argv) cmd_try_alias(int *argc, char ***argv)
{ {
struct options_entry *o; struct options_entry *o;
int old_argc = *argc, new_argc; struct options_array_item *a;
char **old_argv = *argv, **new_argv; int old_argc = *argc, new_argc, i;
u_int size, idx; char **old_argv = *argv, **new_argv;
int i; size_t wanted;
size_t wanted; const char *s, *cp = NULL;
const char *s, *cp = NULL;
o = options_get_only(global_options, "command-alias"); o = options_get_only(global_options, "command-alias");
if (o == NULL || options_array_size(o, &size) == -1 || size == 0) if (o == NULL)
return (-1); return (-1);
wanted = strlen(old_argv[0]); wanted = strlen(old_argv[0]);
for (idx = 0; idx < size; idx++) {
s = options_array_get(o, idx);
if (s == NULL)
continue;
cp = strchr(s, '='); a = options_array_first(o);
if (cp == NULL || (size_t)(cp - s) != wanted) while (a != NULL) {
continue; s = options_array_item_value(a);
if (strncmp(old_argv[0], s, wanted) == 0) if (s != NULL) {
break; cp = strchr(s, '=');
if (cp != NULL &&
(size_t)(cp - s) == wanted &&
strncmp(old_argv[0], s, wanted) == 0)
break;
}
a = options_array_next(a);
} }
if (idx == size) if (a == NULL)
return (-1); return (-1);
if (cmd_string_split(cp + 1, &new_argc, &new_argv) != 0) if (cmd_string_split(cp + 1, &new_argc, &new_argv) != 0)

View File

@ -174,22 +174,26 @@ environ_unset(struct environ *env, const char *name)
void void
environ_update(struct options *oo, struct environ *src, struct environ *dst) environ_update(struct options *oo, struct environ *src, struct environ *dst)
{ {
struct environ_entry *envent; struct environ_entry *envent;
struct options_entry *o; struct options_entry *o;
u_int size, idx; struct options_array_item *a;
const char *value; const char *value;
o = options_get(oo, "update-environment"); o = options_get(oo, "update-environment");
if (o == NULL || options_array_size(o, &size) == -1) if (o == NULL)
return; return;
for (idx = 0; idx < size; idx++) { a = options_array_first(o);
value = options_array_get(o, idx); while (a != NULL) {
if (value == NULL) value = options_array_item_value(a);
if (value == NULL) {
a = options_array_next(a);
continue; continue;
}
if ((envent = environ_find(src, value)) == NULL) if ((envent = environ_find(src, value)) == NULL)
environ_clear(dst, value); environ_clear(dst, value);
else else
environ_set(dst, envent->name, "%s", envent->value); environ_set(dst, envent->name, "%s", envent->value);
a = options_array_next(a);
} }
} }

162
options.c
View File

@ -30,6 +30,23 @@
* a red-black tree. * a red-black tree.
*/ */
struct options_array_item {
u_int index;
char *value;
RB_ENTRY(options_array_item) entry;
};
RB_HEAD(options_array, options_array_item);
static int
options_array_cmp(struct options_array_item *a1, struct options_array_item *a2)
{
if (a1->index < a2->index)
return (-1);
if (a1->index > a2->index)
return (1);
return (0);
}
RB_GENERATE_STATIC(options_array, options_array_item, entry, options_array_cmp);
struct options_entry { struct options_entry {
struct options *owner; struct options *owner;
@ -40,10 +57,7 @@ struct options_entry {
char *string; char *string;
long long number; long long number;
struct style style; struct style style;
struct { struct options_array array;
const char **array;
u_int arraysize;
};
}; };
RB_ENTRY(options_entry) entry; RB_ENTRY(options_entry) entry;
@ -56,8 +70,6 @@ struct options {
static struct options_entry *options_add(struct options *, const char *); static struct options_entry *options_add(struct options *, const char *);
#define OPTIONS_ARRAY_LIMIT 1000
#define OPTIONS_IS_STRING(o) \ #define OPTIONS_IS_STRING(o) \
((o)->tableentry == NULL || \ ((o)->tableentry == NULL || \
(o)->tableentry->type == OPTIONS_TABLE_STRING) (o)->tableentry->type == OPTIONS_TABLE_STRING)
@ -163,6 +175,9 @@ options_empty(struct options *oo, const struct options_table_entry *oe)
o = options_add(oo, oe->name); o = options_add(oo, oe->name);
o->tableentry = oe; o->tableentry = oe;
if (oe->type == OPTIONS_TABLE_ARRAY)
RB_INIT(&o->array);
return (o); return (o);
} }
@ -210,15 +225,11 @@ void
options_remove(struct options_entry *o) options_remove(struct options_entry *o)
{ {
struct options *oo = o->owner; struct options *oo = o->owner;
u_int i;
if (OPTIONS_IS_STRING(o)) if (OPTIONS_IS_STRING(o))
free((void *)o->string); free(o->string);
else if (OPTIONS_IS_ARRAY(o)) { else if (OPTIONS_IS_ARRAY(o))
for (i = 0; i < o->arraysize; i++) options_array_clear(o);
free((void *)o->array[i]);
free(o->array);
}
RB_REMOVE(options_tree, &oo->tree, o); RB_REMOVE(options_tree, &oo->tree, o);
free(o); free(o);
@ -236,62 +247,79 @@ options_table_entry(struct options_entry *o)
return (o->tableentry); return (o->tableentry);
} }
static struct options_array_item *
options_array_item(struct options_entry *o, u_int idx)
{
struct options_array_item a;
a.index = idx;
return (RB_FIND(options_array, &o->array, &a));
}
static void
options_array_free(struct options_entry *o, struct options_array_item *a)
{
free(a->value);
RB_REMOVE(options_array, &o->array, a);
free(a);
}
void void
options_array_clear(struct options_entry *o) options_array_clear(struct options_entry *o)
{ {
if (OPTIONS_IS_ARRAY(o)) struct options_array_item *a, *a1;
o->arraysize = 0;
if (!OPTIONS_IS_ARRAY(o))
return;
RB_FOREACH_SAFE(a, options_array, &o->array, a1)
options_array_free(o, a);
} }
const char * const char *
options_array_get(struct options_entry *o, u_int idx) options_array_get(struct options_entry *o, u_int idx)
{ {
struct options_array_item *a;
if (!OPTIONS_IS_ARRAY(o)) if (!OPTIONS_IS_ARRAY(o))
return (NULL); return (NULL);
if (idx >= o->arraysize) a = options_array_item(o, idx);
if (a == NULL)
return (NULL); return (NULL);
return (o->array[idx]); return (a->value);
} }
int int
options_array_set(struct options_entry *o, u_int idx, const char *value, options_array_set(struct options_entry *o, u_int idx, const char *value,
int append) int append)
{ {
char *new; struct options_array_item *a;
u_int i; char *new;
if (!OPTIONS_IS_ARRAY(o)) if (!OPTIONS_IS_ARRAY(o))
return (-1); return (-1);
if (idx >= OPTIONS_ARRAY_LIMIT) a = options_array_item(o, idx);
return (-1); if (value == NULL) {
if (idx >= o->arraysize) { if (a != NULL)
o->array = xreallocarray(o->array, idx + 1, sizeof *o->array); options_array_free(o, a);
for (i = o->arraysize; i < idx + 1; i++) return (0);
o->array[i] = NULL;
o->arraysize = idx + 1;
} }
new = NULL; if (a == NULL) {
if (value != NULL) { a = xcalloc(1, sizeof *a);
if (o->array[idx] != NULL && append) a->index = idx;
xasprintf(&new, "%s%s", o->array[idx], value); a->value = xstrdup(value);
RB_INSERT(options_array, &o->array, a);
} else {
free(a->value);
if (a != NULL && append)
xasprintf(&new, "%s%s", a->value, value);
else else
new = xstrdup(value); new = xstrdup(value);
a->value = new;
} }
free((void *)o->array[idx]);
o->array[idx] = new;
return (0);
}
int
options_array_size(struct options_entry *o, u_int *size)
{
if (!OPTIONS_IS_ARRAY(o))
return (-1);
if (size != NULL)
*size = o->arraysize;
return (0); return (0);
} }
@ -310,37 +338,69 @@ options_array_assign(struct options_entry *o, const char *s)
while ((next = strsep(&string, separator)) != NULL) { while ((next = strsep(&string, separator)) != NULL) {
if (*next == '\0') if (*next == '\0')
continue; continue;
for (i = 0; i < OPTIONS_ARRAY_LIMIT; i++) { for (i = 0; i < UINT_MAX; i++) {
if (i >= o->arraysize || o->array[i] == NULL) if (options_array_item(o, i) == NULL)
break; break;
} }
if (i == OPTIONS_ARRAY_LIMIT) if (i == UINT_MAX)
break; break;
options_array_set(o, i, next, 0); options_array_set(o, i, next, 0);
} }
free(copy); free(copy);
} }
struct options_array_item *
options_array_first(struct options_entry *o)
{
if (!OPTIONS_IS_ARRAY(o))
return (NULL);
return (RB_MIN(options_array, &o->array));
}
struct options_array_item *
options_array_next(struct options_array_item *a)
{
return (RB_NEXT(options_array, &o->array, a));
}
u_int
options_array_item_index(struct options_array_item *a)
{
return (a->index);
}
const char *
options_array_item_value(struct options_array_item *a)
{
return (a->value);
}
int
options_isarray(struct options_entry *o)
{
return (OPTIONS_IS_ARRAY(o));
}
int int
options_isstring(struct options_entry *o) options_isstring(struct options_entry *o)
{ {
if (o->tableentry == NULL)
return (1);
return (OPTIONS_IS_STRING(o) || OPTIONS_IS_ARRAY(o)); return (OPTIONS_IS_STRING(o) || OPTIONS_IS_ARRAY(o));
} }
const char * const char *
options_tostring(struct options_entry *o, int idx, int numeric) options_tostring(struct options_entry *o, int idx, int numeric)
{ {
static char s[1024]; static char s[1024];
const char *tmp; const char *tmp;
struct options_array_item *a;
if (OPTIONS_IS_ARRAY(o)) { if (OPTIONS_IS_ARRAY(o)) {
if (idx == -1) if (idx == -1)
return (NULL); return (NULL);
if ((u_int)idx >= o->arraysize || o->array[idx] == NULL) a = options_array_item(o, idx);
if (a == NULL)
return (""); return ("");
return (o->array[idx]); return (a->value);
} }
if (OPTIONS_IS_STYLE(o)) if (OPTIONS_IS_STYLE(o))
return (style_tostring(&o->style)); return (style_tostring(&o->style));

View File

@ -1510,9 +1510,10 @@ status_prompt_complete_list(u_int *size, const char *s)
const char **layout, *value, *cp; const char **layout, *value, *cp;
const struct cmd_entry **cmdent; const struct cmd_entry **cmdent;
const struct options_table_entry *oe; const struct options_table_entry *oe;
u_int items, idx; u_int idx;
size_t slen = strlen(s), valuelen; size_t slen = strlen(s), valuelen;
struct options_entry *o; struct options_entry *o;
struct options_array_item *a;
const char *layouts[] = { const char *layouts[] = {
"even-horizontal", "even-vertical", "main-horizontal", "even-horizontal", "even-vertical", "main-horizontal",
"main-vertical", "tiled", NULL "main-vertical", "tiled", NULL
@ -1538,16 +1539,22 @@ status_prompt_complete_list(u_int *size, const char *s)
} }
} }
o = options_get_only(global_options, "command-alias"); o = options_get_only(global_options, "command-alias");
if (o != NULL && options_array_size(o, &items) != -1) { if (o != NULL) {
for (idx = 0; idx < items; idx++) { a = options_array_first(o);
value = options_array_get(o, idx); while (a != NULL) {
value = options_array_item_value(a);;
if (value == NULL || (cp = strchr(value, '=')) == NULL) if (value == NULL || (cp = strchr(value, '=')) == NULL)
continue; goto next;
valuelen = cp - value; valuelen = cp - value;
if (slen > valuelen || strncmp(value, s, slen) != 0) if (slen > valuelen || strncmp(value, s, slen) != 0)
continue; goto next;
list = xreallocarray(list, (*size) + 1, sizeof *list); list = xreallocarray(list, (*size) + 1, sizeof *list);
list[(*size)++] = xstrndup(value, valuelen); list[(*size)++] = xstrndup(value, valuelen);
next:
a = options_array_next(a);
} }
} }
for (idx = 0; idx < (*size); idx++) for (idx = 0; idx < (*size); idx++)

7
tmux.h
View File

@ -50,6 +50,7 @@ struct mode_tree_data;
struct mouse_event; struct mouse_event;
struct options; struct options;
struct options_entry; struct options_entry;
struct options_array_item;
struct session; struct session;
struct tmuxpeer; struct tmuxpeer;
struct tmuxproc; struct tmuxproc;
@ -1642,8 +1643,12 @@ void options_array_clear(struct options_entry *);
const char *options_array_get(struct options_entry *, u_int); const char *options_array_get(struct options_entry *, u_int);
int options_array_set(struct options_entry *, u_int, const char *, int options_array_set(struct options_entry *, u_int, const char *,
int); int);
int options_array_size(struct options_entry *, u_int *);
void options_array_assign(struct options_entry *, const char *); void options_array_assign(struct options_entry *, const char *);
struct options_array_item *options_array_first(struct options_entry *);
struct options_array_item *options_array_next(struct options_array_item *);
u_int options_array_item_index(struct options_array_item *);
const char *options_array_item_value(struct options_array_item *);
int options_isarray(struct options_entry *);
int options_isstring(struct options_entry *); int options_isstring(struct options_entry *);
const char *options_tostring(struct options_entry *, int, int); const char *options_tostring(struct options_entry *, int, int);
char *options_parse(const char *, int *); char *options_parse(const char *, int *);

View File

@ -398,9 +398,10 @@ tty_keys_build(struct tty *tty)
{ {
const struct tty_default_key_raw *tdkr; const struct tty_default_key_raw *tdkr;
const struct tty_default_key_code *tdkc; const struct tty_default_key_code *tdkc;
u_int i, size; u_int i;
const char *s, *value; const char *s, *value;
struct options_entry *o; struct options_entry *o;
struct options_array_item *a;
if (tty->key_tree != NULL) if (tty->key_tree != NULL)
tty_keys_free(tty); tty_keys_free(tty);
@ -423,11 +424,13 @@ tty_keys_build(struct tty *tty)
} }
o = options_get(global_options, "user-keys"); o = options_get(global_options, "user-keys");
if (o != NULL && options_array_size(o, &size) != -1) { if (o != NULL) {
for (i = 0; i < size; i++) { a = options_array_first(o);
value = options_array_get(o, i); while (a != NULL) {
value = options_array_item_value(a);
if (value != NULL) if (value != NULL)
tty_keys_add(tty, value, KEYC_USER + i); tty_keys_add(tty, value, KEYC_USER + i);
a = options_array_next(a);
} }
} }
} }

View File

@ -416,7 +416,8 @@ tty_term_find(char *name, int fd, char **cause)
const struct tty_term_code_entry *ent; const struct tty_term_code_entry *ent;
struct tty_code *code; struct tty_code *code;
struct options_entry *o; struct options_entry *o;
u_int size, i; struct options_array_item *a;
u_int i;
int n, error; int n, error;
const char *s, *acs; const char *s, *acs;
@ -491,12 +492,12 @@ tty_term_find(char *name, int fd, char **cause)
/* Apply terminal overrides. */ /* Apply terminal overrides. */
o = options_get_only(global_options, "terminal-overrides"); o = options_get_only(global_options, "terminal-overrides");
if (options_array_size(o, &size) != -1) { a = options_array_first(o);
for (i = 0; i < size; i++) { while (a != NULL) {
s = options_array_get(o, i); s = options_array_item_value(a);
if (s != NULL) if (s != NULL)
tty_term_override(term, s); tty_term_override(term, s);
} a = options_array_next(a);
} }
/* Delete curses data. */ /* Delete curses data. */