mirror of
				https://github.com/tmux/tmux.git
				synced 2025-11-04 09:26:05 +00:00 
			
		
		
		
	Major tidy up and rework of options tree and set-option/show-options
commands this pushes more of the code into options.c and ties it more closely to the options table rather than having an unnecessary split. Also add support for array options (will be used later). Only (intentional) user visible change is that show-options output is now passed through vis(3) with VIS_DQ so quotes are escaped.
This commit is contained in:
		
							
								
								
									
										673
									
								
								options.c
									
									
									
									
									
								
							
							
						
						
									
										673
									
								
								options.c
									
									
									
									
									
								
							@@ -18,6 +18,7 @@
 | 
			
		||||
 | 
			
		||||
#include <sys/types.h>
 | 
			
		||||
 | 
			
		||||
#include <ctype.h>
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
@@ -29,18 +30,72 @@
 | 
			
		||||
 * a red-black tree.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
struct options {
 | 
			
		||||
	RB_HEAD(options_tree, options_entry) tree;
 | 
			
		||||
	struct options	*parent;
 | 
			
		||||
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;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int	options_cmp(struct options_entry *, struct options_entry *);
 | 
			
		||||
RB_GENERATE_STATIC(options_tree, options_entry, entry, options_cmp);
 | 
			
		||||
struct options {
 | 
			
		||||
	RB_HEAD(options_tree, option)		 tree;
 | 
			
		||||
	struct options				*parent;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct option	*options_add(struct options *, const char *);
 | 
			
		||||
 | 
			
		||||
#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
 | 
			
		||||
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 *
 | 
			
		||||
@@ -54,181 +109,549 @@ options_create(struct options *parent)
 | 
			
		||||
	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
 | 
			
		||||
options_free(struct options *oo)
 | 
			
		||||
{
 | 
			
		||||
	struct options_entry	*o, *o1;
 | 
			
		||||
	struct option	*o, *tmp;
 | 
			
		||||
 | 
			
		||||
	RB_FOREACH_SAFE (o, options_tree, &oo->tree, o1)
 | 
			
		||||
		options_free1(oo, o);
 | 
			
		||||
	RB_FOREACH_SAFE (o, options_tree, &oo->tree, tmp)
 | 
			
		||||
		options_remove(o);
 | 
			
		||||
	free(oo);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct options_entry *
 | 
			
		||||
struct option *
 | 
			
		||||
options_first(struct options *oo)
 | 
			
		||||
{
 | 
			
		||||
	return (RB_MIN(options_tree, &oo->tree));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct options_entry *
 | 
			
		||||
options_next(struct options_entry *o)
 | 
			
		||||
struct option *
 | 
			
		||||
options_next(struct option *o)
 | 
			
		||||
{
 | 
			
		||||
	return (RB_NEXT(options_tree, &oo->tree, o));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct options_entry *
 | 
			
		||||
options_find1(struct options *oo, const char *name)
 | 
			
		||||
struct option *
 | 
			
		||||
options_get_only(struct options *oo, const char *name)
 | 
			
		||||
{
 | 
			
		||||
	struct options_entry	p;
 | 
			
		||||
	struct option	o;
 | 
			
		||||
 | 
			
		||||
	p.name = (char *)name;
 | 
			
		||||
	return (RB_FIND(options_tree, &oo->tree, &p));
 | 
			
		||||
	o.name = name;
 | 
			
		||||
	return (RB_FIND(options_tree, &oo->tree, &o));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct options_entry *
 | 
			
		||||
options_find(struct options *oo, const char *name)
 | 
			
		||||
struct option *
 | 
			
		||||
options_get(struct options *oo, const char *name)
 | 
			
		||||
{
 | 
			
		||||
	struct options_entry	*o, p;
 | 
			
		||||
	struct option	*o;
 | 
			
		||||
 | 
			
		||||
	p.name = (char *)name;
 | 
			
		||||
	o = RB_FIND(options_tree, &oo->tree, &p);
 | 
			
		||||
	o = options_get_only(oo, name);
 | 
			
		||||
	while (o == NULL) {
 | 
			
		||||
		oo = oo->parent;
 | 
			
		||||
		if (oo == NULL)
 | 
			
		||||
			break;
 | 
			
		||||
		o = RB_FIND(options_tree, &oo->tree, &p);
 | 
			
		||||
		o = options_get_only(oo, name);
 | 
			
		||||
	}
 | 
			
		||||
	return (o);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct option *
 | 
			
		||||
options_empty(struct options *oo, const struct options_table_entry *oe)
 | 
			
		||||
{
 | 
			
		||||
	struct option	*o;
 | 
			
		||||
 | 
			
		||||
	o = options_add(oo, oe->name);
 | 
			
		||||
	o->tableentry = oe;
 | 
			
		||||
 | 
			
		||||
	return (o);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct option *
 | 
			
		||||
options_default(struct options *oo, const struct options_table_entry *oe)
 | 
			
		||||
{
 | 
			
		||||
	struct option	*o;
 | 
			
		||||
	char		*cp, *copy, *next;
 | 
			
		||||
	u_int		 idx = 0;
 | 
			
		||||
 | 
			
		||||
	o = options_empty(oo, oe);
 | 
			
		||||
 | 
			
		||||
	if (oe->type == OPTIONS_TABLE_ARRAY) {
 | 
			
		||||
		copy = cp = xstrdup(oe->default_str);
 | 
			
		||||
		while ((next = strsep(&cp, ",")) != NULL) {
 | 
			
		||||
			options_array_set(o, idx, next);
 | 
			
		||||
			idx++;
 | 
			
		||||
		}
 | 
			
		||||
		free(copy);
 | 
			
		||||
		return (o);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (oe->type == OPTIONS_TABLE_STRING)
 | 
			
		||||
		o->string = xstrdup(oe->default_str);
 | 
			
		||||
	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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
options_remove(struct options *oo, const char *name)
 | 
			
		||||
options_remove(struct option *o)
 | 
			
		||||
{
 | 
			
		||||
	struct options_entry	*o;
 | 
			
		||||
	struct options	*oo = o->owner;
 | 
			
		||||
	u_int		 i;
 | 
			
		||||
 | 
			
		||||
	if ((o = options_find1(oo, name)) != NULL)
 | 
			
		||||
		options_free1(oo, o);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct options_entry *
 | 
			
		||||
options_set_string(struct options *oo, const char *name, int append,
 | 
			
		||||
    const char *fmt, ...)
 | 
			
		||||
{
 | 
			
		||||
	struct options_entry	*o;
 | 
			
		||||
	va_list			 ap;
 | 
			
		||||
	char			*s, *value;
 | 
			
		||||
 | 
			
		||||
	va_start(ap, fmt);
 | 
			
		||||
	xvasprintf(&s, fmt, ap);
 | 
			
		||||
	va_end(ap);
 | 
			
		||||
 | 
			
		||||
	o = options_find1(oo, name);
 | 
			
		||||
	if (o == NULL || !append)
 | 
			
		||||
		value = s;
 | 
			
		||||
	else {
 | 
			
		||||
		xasprintf(&value, "%s%s", o->str, s);
 | 
			
		||||
		free(s);
 | 
			
		||||
	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);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	o = options_new(oo, name);
 | 
			
		||||
	o->type = OPTIONS_STRING;
 | 
			
		||||
	o->str = value;
 | 
			
		||||
	RB_REMOVE(options_tree, &oo->tree, o);
 | 
			
		||||
	free(o);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *
 | 
			
		||||
options_name(struct option *o)
 | 
			
		||||
{
 | 
			
		||||
	return (o->name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const struct options_table_entry *
 | 
			
		||||
options_table_entry(struct option *o)
 | 
			
		||||
{
 | 
			
		||||
	return (o->tableentry);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *
 | 
			
		||||
options_array_get(struct option *o, u_int idx)
 | 
			
		||||
{
 | 
			
		||||
	if (!OPTIONS_IS_ARRAY(o))
 | 
			
		||||
		return (NULL);
 | 
			
		||||
	if (idx >= o->arraysize)
 | 
			
		||||
		return (NULL);
 | 
			
		||||
	return (o->array[idx]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
options_array_set(struct option *o, u_int idx, const char *value)
 | 
			
		||||
{
 | 
			
		||||
	u_int	i;
 | 
			
		||||
 | 
			
		||||
	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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
options_array_size(struct option *o, u_int *size)
 | 
			
		||||
{
 | 
			
		||||
	if (!OPTIONS_IS_ARRAY(o))
 | 
			
		||||
		return (-1);
 | 
			
		||||
	if (size != NULL)
 | 
			
		||||
		*size = o->arraysize;
 | 
			
		||||
	return (0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
options_isstring(struct option *o)
 | 
			
		||||
{
 | 
			
		||||
	if (o->tableentry == NULL)
 | 
			
		||||
		return (1);
 | 
			
		||||
	return (OPTIONS_IS_STRING(o) || OPTIONS_IS_ARRAY(o));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const char *
 | 
			
		||||
options_tostring(struct option *o, int idx)
 | 
			
		||||
{
 | 
			
		||||
	static char	 s[1024];
 | 
			
		||||
	const char	*tmp;
 | 
			
		||||
 | 
			
		||||
	if (OPTIONS_IS_ARRAY(o)) {
 | 
			
		||||
		if (idx == -1)
 | 
			
		||||
			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
 | 
			
		||||
		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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 (xstrdup(found->name));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct option *
 | 
			
		||||
options_match_get(struct options *oo, const char *s, int *idx, int only,
 | 
			
		||||
    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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const char *
 | 
			
		||||
options_get_string(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);
 | 
			
		||||
	if (o->type != OPTIONS_STRING)
 | 
			
		||||
		fatalx("option %s not a string", name);
 | 
			
		||||
	return (o->str);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct options_entry *
 | 
			
		||||
options_set_number(struct options *oo, const char *name, long long value)
 | 
			
		||||
{
 | 
			
		||||
	struct options_entry	*o;
 | 
			
		||||
 | 
			
		||||
	o = options_new(oo, name);
 | 
			
		||||
	o->type = OPTIONS_NUMBER;
 | 
			
		||||
	o->num = value;
 | 
			
		||||
 | 
			
		||||
	return (o);
 | 
			
		||||
	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 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);
 | 
			
		||||
	if (o->type != OPTIONS_NUMBER)
 | 
			
		||||
		fatalx("option %s not a number", name);
 | 
			
		||||
	return (o->num);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct options_entry *
 | 
			
		||||
options_set_style(struct options *oo, const char *name, int append,
 | 
			
		||||
    const char *value)
 | 
			
		||||
{
 | 
			
		||||
	struct options_entry	*o;
 | 
			
		||||
	struct grid_cell	 tmpgc;
 | 
			
		||||
 | 
			
		||||
	o = options_find1(oo, name);
 | 
			
		||||
	if (o == NULL || !append)
 | 
			
		||||
		memcpy(&tmpgc, &grid_default_cell, sizeof tmpgc);
 | 
			
		||||
	else
 | 
			
		||||
		memcpy(&tmpgc, &o->style, sizeof tmpgc);
 | 
			
		||||
 | 
			
		||||
	if (style_parse(&grid_default_cell, &tmpgc, value) == -1)
 | 
			
		||||
		return (NULL);
 | 
			
		||||
 | 
			
		||||
	o = options_new(oo, name);
 | 
			
		||||
	o->type = OPTIONS_STYLE;
 | 
			
		||||
	memcpy(&o->style, &tmpgc, sizeof o->style);
 | 
			
		||||
 | 
			
		||||
	return (o);
 | 
			
		||||
	if (!OPTIONS_IS_NUMBER(o))
 | 
			
		||||
	    fatalx("option %s is not a number", name);
 | 
			
		||||
	return (o->number);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const struct grid_cell *
 | 
			
		||||
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);
 | 
			
		||||
	if (o->type != OPTIONS_STYLE)
 | 
			
		||||
		fatalx("option %s not a style", name);
 | 
			
		||||
	if (!OPTIONS_IS_STYLE(o))
 | 
			
		||||
		fatalx("option %s is not a style", name);
 | 
			
		||||
	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);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user