xref: /netbsd-src/external/bsd/tmux/dist/style.c (revision d90047b5d07facf36e6c01dcc0bded8997ce9cc2)
1 /* $OpenBSD$ */
2 
3 /*
4  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
5  * Copyright (c) 2014 Tiago Cunha <tcunha@users.sourceforge.net>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
16  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
17  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/types.h>
21 
22 #include <ctype.h>
23 #include <stdlib.h>
24 #include <string.h>
25 
26 #include "tmux.h"
27 
28 /* Mask for bits not included in style. */
29 #define STYLE_ATTR_MASK (~GRID_ATTR_CHARSET)
30 
31 /* Default style. */
32 static struct style style_default = {
33 	{ { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0  },
34 
35 	8,
36 	STYLE_ALIGN_DEFAULT,
37 	STYLE_LIST_OFF,
38 
39 	STYLE_RANGE_NONE, 0
40 };
41 
42 /*
43  * Parse an embedded style of the form "fg=colour,bg=colour,bright,...".
44  * Note that this adds onto the given style, so it must have been initialized
45  * alredy.
46  */
47 int
48 style_parse(struct style *sy, const struct grid_cell *base, const char *in)
49 {
50 	struct style	saved;
51 	const char	delimiters[] = " ,", *cp;
52 	char		tmp[256], *found;
53 	int		value;
54 	size_t		end;
55 
56 	if (*in == '\0')
57 		return (0);
58 	style_copy(&saved, sy);
59 
60 	do {
61 		while (*in != '\0' && strchr(delimiters, *in) != NULL)
62 			in++;
63 		if (*in == '\0')
64 			break;
65 
66 		end = strcspn(in, delimiters);
67 		if (end > (sizeof tmp) - 1)
68 			goto error;
69 		memcpy(tmp, in, end);
70 		tmp[end] = '\0';
71 
72 		if (strcasecmp(tmp, "default") == 0) {
73 			sy->gc.fg = base->fg;
74 			sy->gc.bg = base->bg;
75 			sy->gc.attr = base->attr;
76 			sy->gc.flags = base->flags;
77 		} else if (strcasecmp(tmp, "nolist") == 0)
78 			sy->list = STYLE_LIST_OFF;
79 		else if (strncasecmp(tmp, "list=", 5) == 0) {
80 			if (strcasecmp(tmp + 5, "on") == 0)
81 				sy->list = STYLE_LIST_ON;
82 			else if (strcasecmp(tmp + 5, "focus") == 0)
83 				sy->list = STYLE_LIST_FOCUS;
84 			else if (strcasecmp(tmp + 5, "left-marker") == 0)
85 				sy->list = STYLE_LIST_LEFT_MARKER;
86 			else if (strcasecmp(tmp + 5, "right-marker") == 0)
87 				sy->list = STYLE_LIST_RIGHT_MARKER;
88 			else
89 				goto error;
90 		} else if (strcasecmp(tmp, "norange") == 0) {
91 			sy->range_type = style_default.range_type;
92 			sy->range_argument = style_default.range_type;
93 		} else if (end > 6 && strncasecmp(tmp, "range=", 6) == 0) {
94 			found = strchr(tmp + 6, '|');
95 			if (found != NULL) {
96 				*found++ = '\0';
97 				if (*found == '\0')
98 					goto error;
99 				for (cp = found; *cp != '\0'; cp++) {
100 					if (!isdigit((u_char)*cp))
101 						goto error;
102 				}
103 			}
104 			if (strcasecmp(tmp + 6, "left") == 0) {
105 				if (found != NULL)
106 					goto error;
107 				sy->range_type = STYLE_RANGE_LEFT;
108 				sy->range_argument = 0;
109 			} else if (strcasecmp(tmp + 6, "right") == 0) {
110 				if (found != NULL)
111 					goto error;
112 				sy->range_type = STYLE_RANGE_RIGHT;
113 				sy->range_argument = 0;
114 			} else if (strcasecmp(tmp + 6, "window") == 0) {
115 				if (found == NULL)
116 					goto error;
117 				sy->range_type = STYLE_RANGE_WINDOW;
118 				sy->range_argument = atoi(found);
119 			}
120 		} else if (strcasecmp(tmp, "noalign") == 0)
121 			sy->align = style_default.align;
122 		else if (end > 6 && strncasecmp(tmp, "align=", 6) == 0) {
123 			if (strcasecmp(tmp + 6, "left") == 0)
124 				sy->align = STYLE_ALIGN_LEFT;
125 			else if (strcasecmp(tmp + 6, "centre") == 0)
126 				sy->align = STYLE_ALIGN_CENTRE;
127 			else if (strcasecmp(tmp + 6, "right") == 0)
128 				sy->align = STYLE_ALIGN_RIGHT;
129 			else
130 				goto error;
131 		} else if (end > 5 && strncasecmp(tmp, "fill=", 5) == 0) {
132 			if ((value = colour_fromstring(tmp + 5)) == -1)
133 				goto error;
134 			sy->fill = value;
135 		} else if (end > 3 && strncasecmp(tmp + 1, "g=", 2) == 0) {
136 			if ((value = colour_fromstring(tmp + 3)) == -1)
137 				goto error;
138 			if (*in == 'f' || *in == 'F') {
139 				if (value != 8)
140 					sy->gc.fg = value;
141 				else
142 					sy->gc.fg = base->fg;
143 			} else if (*in == 'b' || *in == 'B') {
144 				if (value != 8)
145 					sy->gc.bg = value;
146 				else
147 					sy->gc.bg = base->bg;
148 			} else
149 				goto error;
150 		} else if (strcasecmp(tmp, "none") == 0)
151 			sy->gc.attr = 0;
152 		else if (end > 2 && strncasecmp(tmp, "no", 2) == 0) {
153 			if ((value = attributes_fromstring(tmp + 2)) == -1)
154 				goto error;
155 			sy->gc.attr &= ~value;
156 		} else {
157 			if ((value = attributes_fromstring(tmp)) == -1)
158 				goto error;
159 			sy->gc.attr |= value;
160 		}
161 
162 		in += end + strspn(in + end, delimiters);
163 	} while (*in != '\0');
164 
165 	return (0);
166 
167 error:
168 	style_copy(sy, &saved);
169 	return (-1);
170 }
171 
172 /* Convert style to a string. */
173 const char *
174 style_tostring(struct style *sy)
175 {
176 	struct grid_cell	*gc = &sy->gc;
177 	int			 off = 0;
178 	const char		*comma = "", *tmp = "";
179 	static char		 s[256];
180 	char			 b[16];
181 
182 	*s = '\0';
183 
184 	if (sy->list != STYLE_LIST_OFF) {
185 		if (sy->list == STYLE_LIST_ON)
186 			tmp = "on";
187 		else if (sy->list == STYLE_LIST_FOCUS)
188 			tmp = "focus";
189 		else if (sy->list == STYLE_LIST_LEFT_MARKER)
190 			tmp = "left-marker";
191 		else if (sy->list == STYLE_LIST_RIGHT_MARKER)
192 			tmp = "right-marker";
193 		else
194 			abort();	// XXX: gcc
195 		off += xsnprintf(s + off, sizeof s - off, "%slist=%s", comma,
196 		    tmp);
197 		comma = ",";
198 	}
199 	if (sy->range_type != STYLE_RANGE_NONE) {
200 		if (sy->range_type == STYLE_RANGE_LEFT)
201 			tmp = "left";
202 		else if (sy->range_type == STYLE_RANGE_RIGHT)
203 			tmp = "right";
204 		else if (sy->range_type == STYLE_RANGE_WINDOW) {
205 			snprintf(b, sizeof b, "window|%u", sy->range_argument);
206 			tmp = b;
207 		} else
208 			abort();	// XXX: gcc
209 		off += xsnprintf(s + off, sizeof s - off, "%srange=%s", comma,
210 		    tmp);
211 		comma = ",";
212 	}
213 	if (sy->align != STYLE_ALIGN_DEFAULT) {
214 		if (sy->align == STYLE_ALIGN_LEFT)
215 			tmp = "left";
216 		else if (sy->align == STYLE_ALIGN_CENTRE)
217 			tmp = "centre";
218 		else if (sy->align == STYLE_ALIGN_RIGHT)
219 			tmp = "right";
220 		else
221 			abort();	// XXX: gcc
222 		off += xsnprintf(s + off, sizeof s - off, "%salign=%s", comma,
223 		    tmp);
224 		comma = ",";
225 	}
226 	if (sy->fill != 8) {
227 		off += xsnprintf(s + off, sizeof s - off, "%sfill=%s", comma,
228 		    colour_tostring(sy->fill));
229 		comma = ",";
230 	}
231 	if (gc->fg != 8) {
232 		off += xsnprintf(s + off, sizeof s - off, "%sfg=%s", comma,
233 		    colour_tostring(gc->fg));
234 		comma = ",";
235 	}
236 	if (gc->bg != 8) {
237 		off += xsnprintf(s + off, sizeof s - off, "%sbg=%s", comma,
238 		    colour_tostring(gc->bg));
239 		comma = ",";
240 	}
241 	if (gc->attr != 0 && gc->attr != GRID_ATTR_CHARSET) {
242 		xsnprintf(s + off, sizeof s - off, "%s%s", comma,
243 		    attributes_tostring(gc->attr));
244 		comma = ",";
245 	}
246 
247 	if (*s == '\0')
248 		return ("default");
249 	return (s);
250 }
251 
252 /* Apply a style. */
253 void
254 style_apply(struct grid_cell *gc, struct options *oo, const char *name)
255 {
256 	struct style	*sy;
257 
258 	memcpy(gc, &grid_default_cell, sizeof *gc);
259 	sy = options_get_style(oo, name);
260 	gc->fg = sy->gc.fg;
261 	gc->bg = sy->gc.bg;
262 	gc->attr |= sy->gc.attr;
263 }
264 
265 /* Apply a style, updating if default. */
266 void
267 style_apply_update(struct grid_cell *gc, struct options *oo, const char *name)
268 {
269 	struct style	*sy;
270 
271 	sy = options_get_style(oo, name);
272 	if (sy->gc.fg != 8)
273 		gc->fg = sy->gc.fg;
274 	if (sy->gc.bg != 8)
275 		gc->bg = sy->gc.bg;
276 	if (sy->gc.attr != 0)
277 		gc->attr |= sy->gc.attr;
278 }
279 
280 /* Initialize style from cell. */
281 void
282 style_set(struct style *sy, const struct grid_cell *gc)
283 {
284 	memcpy(sy, &style_default, sizeof *sy);
285 	memcpy(&sy->gc, gc, sizeof sy->gc);
286 }
287 
288 /* Copy style. */
289 void
290 style_copy(struct style *dst, struct style *src)
291 {
292 	memcpy(dst, src, sizeof *dst);
293 }
294 
295 /* Check if two styles are (visibly) the same. */
296 int
297 style_equal(struct style *sy1, struct style *sy2)
298 {
299 	struct grid_cell	*gc1 = &sy1->gc;
300 	struct grid_cell	*gc2 = &sy2->gc;
301 
302 	if (gc1->fg != gc2->fg)
303 		return (0);
304 	if (gc1->bg != gc2->bg)
305 		return (0);
306 	if ((gc1->attr & STYLE_ATTR_MASK) != (gc2->attr & STYLE_ATTR_MASK))
307 		return (0);
308 	if (sy1->fill != sy2->fill)
309 		return (0);
310 	if (sy1->align != sy2->align)
311 		return (0);
312 	return (1);
313 }
314 
315 /* Is this style default? */
316 int
317 style_is_default(struct style *sy)
318 {
319 	return (style_equal(sy, &style_default));
320 }
321