xref: /netbsd-src/external/bsd/tmux/dist/style.c (revision 890b6d91a44b7fcb2dfbcbc1e93463086e462d2d)
1928fc495Schristos /* $OpenBSD$ */
2928fc495Schristos 
3928fc495Schristos /*
4ed4e6cd4Schristos  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
5928fc495Schristos  * Copyright (c) 2014 Tiago Cunha <tcunha@users.sourceforge.net>
6928fc495Schristos  *
7928fc495Schristos  * Permission to use, copy, modify, and distribute this software for any
8928fc495Schristos  * purpose with or without fee is hereby granted, provided that the above
9928fc495Schristos  * copyright notice and this permission notice appear in all copies.
10928fc495Schristos  *
11928fc495Schristos  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12928fc495Schristos  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13928fc495Schristos  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14928fc495Schristos  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15928fc495Schristos  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
16928fc495Schristos  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
17928fc495Schristos  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18928fc495Schristos  */
19928fc495Schristos 
205494e770Schristos #include <sys/types.h>
215494e770Schristos 
22ef36e747Schristos #include <ctype.h>
23ef36e747Schristos #include <stdlib.h>
24928fc495Schristos #include <string.h>
25928fc495Schristos 
26928fc495Schristos #include "tmux.h"
27928fc495Schristos 
28ef36e747Schristos /* Mask for bits not included in style. */
29e271dbb8Schristos #define STYLE_ATTR_MASK (~0)
30ef36e747Schristos 
31ef36e747Schristos /* Default style. */
32ef36e747Schristos static struct style style_default = {
33f844e94eSwiz 	{ { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0, 0 },
34e271dbb8Schristos 	0,
35ef36e747Schristos 
3630744affSchristos 	8,
37ef36e747Schristos 	STYLE_ALIGN_DEFAULT,
38ef36e747Schristos 	STYLE_LIST_OFF,
39ef36e747Schristos 
40f844e94eSwiz 	STYLE_RANGE_NONE, 0, "",
4168e6ba84Schristos 
4268e6ba84Schristos 	STYLE_DEFAULT_BASE
43ef36e747Schristos };
44ef36e747Schristos 
45f844e94eSwiz /* Set range string. */
46f844e94eSwiz static void
47f844e94eSwiz style_set_range_string(struct style *sy, const char *s)
48f844e94eSwiz {
49f844e94eSwiz 	strlcpy(sy->range_string, s, sizeof sy->range_string);
50f844e94eSwiz }
51f844e94eSwiz 
52ef36e747Schristos /*
5368e6ba84Schristos  * Parse an embedded style of the form "fg=colour,bg=colour,bright,...".  Note
5468e6ba84Schristos  * that this adds onto the given style, so it must have been initialized
5568e6ba84Schristos  * already.
56ef36e747Schristos  */
57928fc495Schristos int
58ef36e747Schristos style_parse(struct style *sy, const struct grid_cell *base, const char *in)
59928fc495Schristos {
60ef36e747Schristos 	struct style	saved;
61*890b6d91Swiz 	const char	delimiters[] = " ,\n", *errstr;
62ef36e747Schristos 	char		tmp[256], *found;
63ef36e747Schristos 	int		value;
64928fc495Schristos 	size_t		end;
65*890b6d91Swiz 	u_int		n;
66928fc495Schristos 
67928fc495Schristos 	if (*in == '\0')
68928fc495Schristos 		return (0);
69ef36e747Schristos 	style_copy(&saved, sy);
70928fc495Schristos 
71e271dbb8Schristos 	log_debug("%s: %s", __func__, in);
72928fc495Schristos 	do {
7330744affSchristos 		while (*in != '\0' && strchr(delimiters, *in) != NULL)
74ef36e747Schristos 			in++;
75ef36e747Schristos 		if (*in == '\0')
76ef36e747Schristos 			break;
77ef36e747Schristos 
78928fc495Schristos 		end = strcspn(in, delimiters);
79928fc495Schristos 		if (end > (sizeof tmp) - 1)
805494e770Schristos 			goto error;
81928fc495Schristos 		memcpy(tmp, in, end);
82928fc495Schristos 		tmp[end] = '\0';
83928fc495Schristos 
84e271dbb8Schristos 		log_debug("%s: %s", __func__, tmp);
85928fc495Schristos 		if (strcasecmp(tmp, "default") == 0) {
86ef36e747Schristos 			sy->gc.fg = base->fg;
87ef36e747Schristos 			sy->gc.bg = base->bg;
88f844e94eSwiz 			sy->gc.us = base->us;
89ef36e747Schristos 			sy->gc.attr = base->attr;
90ef36e747Schristos 			sy->gc.flags = base->flags;
91e271dbb8Schristos 		} else if (strcasecmp(tmp, "ignore") == 0)
92e271dbb8Schristos 			sy->ignore = 1;
93e271dbb8Schristos 		else if (strcasecmp(tmp, "noignore") == 0)
94e271dbb8Schristos 			sy->ignore = 0;
95e271dbb8Schristos 		else if (strcasecmp(tmp, "push-default") == 0)
9668e6ba84Schristos 			sy->default_type = STYLE_DEFAULT_PUSH;
9768e6ba84Schristos 		else if (strcasecmp(tmp, "pop-default") == 0)
9868e6ba84Schristos 			sy->default_type = STYLE_DEFAULT_POP;
9968e6ba84Schristos 		else if (strcasecmp(tmp, "nolist") == 0)
100ef36e747Schristos 			sy->list = STYLE_LIST_OFF;
101ef36e747Schristos 		else if (strncasecmp(tmp, "list=", 5) == 0) {
102ef36e747Schristos 			if (strcasecmp(tmp + 5, "on") == 0)
103ef36e747Schristos 				sy->list = STYLE_LIST_ON;
104ef36e747Schristos 			else if (strcasecmp(tmp + 5, "focus") == 0)
105ef36e747Schristos 				sy->list = STYLE_LIST_FOCUS;
106ef36e747Schristos 			else if (strcasecmp(tmp + 5, "left-marker") == 0)
107ef36e747Schristos 				sy->list = STYLE_LIST_LEFT_MARKER;
108ef36e747Schristos 			else if (strcasecmp(tmp + 5, "right-marker") == 0)
109ef36e747Schristos 				sy->list = STYLE_LIST_RIGHT_MARKER;
110ef36e747Schristos 			else
111ef36e747Schristos 				goto error;
112ef36e747Schristos 		} else if (strcasecmp(tmp, "norange") == 0) {
113ef36e747Schristos 			sy->range_type = style_default.range_type;
114ef36e747Schristos 			sy->range_argument = style_default.range_type;
115f844e94eSwiz 			strlcpy(sy->range_string, style_default.range_string,
116f844e94eSwiz 			    sizeof sy->range_string);
117ef36e747Schristos 		} else if (end > 6 && strncasecmp(tmp, "range=", 6) == 0) {
118ef36e747Schristos 			found = strchr(tmp + 6, '|');
119ef36e747Schristos 			if (found != NULL) {
120ef36e747Schristos 				*found++ = '\0';
121ef36e747Schristos 				if (*found == '\0')
122ef36e747Schristos 					goto error;
123ef36e747Schristos 			}
124ef36e747Schristos 			if (strcasecmp(tmp + 6, "left") == 0) {
125ef36e747Schristos 				if (found != NULL)
126ef36e747Schristos 					goto error;
127ef36e747Schristos 				sy->range_type = STYLE_RANGE_LEFT;
128ef36e747Schristos 				sy->range_argument = 0;
129f844e94eSwiz 				style_set_range_string(sy, "");
130ef36e747Schristos 			} else if (strcasecmp(tmp + 6, "right") == 0) {
131ef36e747Schristos 				if (found != NULL)
132ef36e747Schristos 					goto error;
133ef36e747Schristos 				sy->range_type = STYLE_RANGE_RIGHT;
134ef36e747Schristos 				sy->range_argument = 0;
135f844e94eSwiz 				style_set_range_string(sy, "");
136f844e94eSwiz 			} else if (strcasecmp(tmp + 6, "pane") == 0) {
137f844e94eSwiz 				if (found == NULL)
138f844e94eSwiz 					goto error;
139f844e94eSwiz 				if (*found != '%' || found[1] == '\0')
140f844e94eSwiz 					goto error;
141*890b6d91Swiz 				n = strtonum(found + 1, 0, UINT_MAX, &errstr);
142*890b6d91Swiz 				if (errstr != NULL)
143f844e94eSwiz 					goto error;
144f844e94eSwiz 				sy->range_type = STYLE_RANGE_PANE;
145*890b6d91Swiz 				sy->range_argument = n;
146f844e94eSwiz 				style_set_range_string(sy, "");
147ef36e747Schristos 			} else if (strcasecmp(tmp + 6, "window") == 0) {
148ef36e747Schristos 				if (found == NULL)
149ef36e747Schristos 					goto error;
150*890b6d91Swiz 				n = strtonum(found, 0, UINT_MAX, &errstr);
151*890b6d91Swiz 				if (errstr != NULL)
152f844e94eSwiz 					goto error;
153ef36e747Schristos 				sy->range_type = STYLE_RANGE_WINDOW;
154*890b6d91Swiz 				sy->range_argument = n;
155f844e94eSwiz 				style_set_range_string(sy, "");
156f844e94eSwiz 			} else if (strcasecmp(tmp + 6, "session") == 0) {
157f844e94eSwiz 				if (found == NULL)
158f844e94eSwiz 					goto error;
159f844e94eSwiz 				if (*found != '$' || found[1] == '\0')
160f844e94eSwiz 					goto error;
161*890b6d91Swiz 				n = strtonum(found + 1, 0, UINT_MAX, &errstr);
162*890b6d91Swiz 				if (errstr != NULL)
163f844e94eSwiz 					goto error;
164f844e94eSwiz 				sy->range_type = STYLE_RANGE_SESSION;
165*890b6d91Swiz 				sy->range_argument = n;
166f844e94eSwiz 				style_set_range_string(sy, "");
167f844e94eSwiz 			} else if (strcasecmp(tmp + 6, "user") == 0) {
168f844e94eSwiz 				if (found == NULL)
169f844e94eSwiz 					goto error;
170f844e94eSwiz 				sy->range_type = STYLE_RANGE_USER;
171f844e94eSwiz 				sy->range_argument = 0;
172f844e94eSwiz 				style_set_range_string(sy, found);
173ef36e747Schristos 			}
174ef36e747Schristos 		} else if (strcasecmp(tmp, "noalign") == 0)
175ef36e747Schristos 			sy->align = style_default.align;
176ef36e747Schristos 		else if (end > 6 && strncasecmp(tmp, "align=", 6) == 0) {
177ef36e747Schristos 			if (strcasecmp(tmp + 6, "left") == 0)
178ef36e747Schristos 				sy->align = STYLE_ALIGN_LEFT;
179ef36e747Schristos 			else if (strcasecmp(tmp + 6, "centre") == 0)
180ef36e747Schristos 				sy->align = STYLE_ALIGN_CENTRE;
181ef36e747Schristos 			else if (strcasecmp(tmp + 6, "right") == 0)
182ef36e747Schristos 				sy->align = STYLE_ALIGN_RIGHT;
183e271dbb8Schristos 			else if (strcasecmp(tmp + 6, "absolute-centre") == 0)
184e271dbb8Schristos 				sy->align = STYLE_ALIGN_ABSOLUTE_CENTRE;
185ef36e747Schristos 			else
186ef36e747Schristos 				goto error;
18730744affSchristos 		} else if (end > 5 && strncasecmp(tmp, "fill=", 5) == 0) {
18830744affSchristos 			if ((value = colour_fromstring(tmp + 5)) == -1)
18930744affSchristos 				goto error;
19030744affSchristos 			sy->fill = value;
191928fc495Schristos 		} else if (end > 3 && strncasecmp(tmp + 1, "g=", 2) == 0) {
192ef36e747Schristos 			if ((value = colour_fromstring(tmp + 3)) == -1)
1935494e770Schristos 				goto error;
194928fc495Schristos 			if (*in == 'f' || *in == 'F') {
195ef36e747Schristos 				if (value != 8)
196ef36e747Schristos 					sy->gc.fg = value;
1974e179ddaSchristos 				else
198ef36e747Schristos 					sy->gc.fg = base->fg;
199928fc495Schristos 			} else if (*in == 'b' || *in == 'B') {
200ef36e747Schristos 				if (value != 8)
201ef36e747Schristos 					sy->gc.bg = value;
2024e179ddaSchristos 				else
203ef36e747Schristos 					sy->gc.bg = base->bg;
204928fc495Schristos 			} else
2055494e770Schristos 				goto error;
206f844e94eSwiz 		} else if (end > 3 && strncasecmp(tmp, "us=", 3) == 0) {
207f844e94eSwiz 			if ((value = colour_fromstring(tmp + 3)) == -1)
208f844e94eSwiz 				goto error;
209f844e94eSwiz 			if (value != 8)
210f844e94eSwiz 				sy->gc.us = value;
211f844e94eSwiz 			else
212f844e94eSwiz 				sy->gc.us = base->us;
213928fc495Schristos 		} else if (strcasecmp(tmp, "none") == 0)
214ef36e747Schristos 			sy->gc.attr = 0;
215928fc495Schristos 		else if (end > 2 && strncasecmp(tmp, "no", 2) == 0) {
216ef36e747Schristos 			if ((value = attributes_fromstring(tmp + 2)) == -1)
2175494e770Schristos 				goto error;
218ef36e747Schristos 			sy->gc.attr &= ~value;
219928fc495Schristos 		} else {
220ef36e747Schristos 			if ((value = attributes_fromstring(tmp)) == -1)
2215494e770Schristos 				goto error;
222ef36e747Schristos 			sy->gc.attr |= value;
223928fc495Schristos 		}
224928fc495Schristos 
225928fc495Schristos 		in += end + strspn(in + end, delimiters);
226928fc495Schristos 	} while (*in != '\0');
227928fc495Schristos 
228928fc495Schristos 	return (0);
2295494e770Schristos 
2305494e770Schristos error:
231ef36e747Schristos 	style_copy(sy, &saved);
2325494e770Schristos 	return (-1);
233928fc495Schristos }
234928fc495Schristos 
235928fc495Schristos /* Convert style to a string. */
236928fc495Schristos const char *
237ef36e747Schristos style_tostring(struct style *sy)
238928fc495Schristos {
239ef36e747Schristos 	struct grid_cell	*gc = &sy->gc;
240ef36e747Schristos 	int			 off = 0;
24130744affSchristos 	const char		*comma = "", *tmp = "";
242928fc495Schristos 	static char		 s[256];
243f844e94eSwiz 	char			 b[21];
244928fc495Schristos 
245928fc495Schristos 	*s = '\0';
246928fc495Schristos 
247ef36e747Schristos 	if (sy->list != STYLE_LIST_OFF) {
248ef36e747Schristos 		if (sy->list == STYLE_LIST_ON)
249ef36e747Schristos 			tmp = "on";
250ef36e747Schristos 		else if (sy->list == STYLE_LIST_FOCUS)
251ef36e747Schristos 			tmp = "focus";
252ef36e747Schristos 		else if (sy->list == STYLE_LIST_LEFT_MARKER)
253ef36e747Schristos 			tmp = "left-marker";
254ef36e747Schristos 		else if (sy->list == STYLE_LIST_RIGHT_MARKER)
255ef36e747Schristos 			tmp = "right-marker";
256ef36e747Schristos 		off += xsnprintf(s + off, sizeof s - off, "%slist=%s", comma,
257ef36e747Schristos 		    tmp);
258ef36e747Schristos 		comma = ",";
259ef36e747Schristos 	}
260ef36e747Schristos 	if (sy->range_type != STYLE_RANGE_NONE) {
261ef36e747Schristos 		if (sy->range_type == STYLE_RANGE_LEFT)
262ef36e747Schristos 			tmp = "left";
263ef36e747Schristos 		else if (sy->range_type == STYLE_RANGE_RIGHT)
264ef36e747Schristos 			tmp = "right";
265f844e94eSwiz 		else if (sy->range_type == STYLE_RANGE_PANE) {
266f844e94eSwiz 			snprintf(b, sizeof b, "pane|%%%u", sy->range_argument);
267f844e94eSwiz 			tmp = b;
268f844e94eSwiz 		} else if (sy->range_type == STYLE_RANGE_WINDOW) {
269ef36e747Schristos 			snprintf(b, sizeof b, "window|%u", sy->range_argument);
270ef36e747Schristos 			tmp = b;
271f844e94eSwiz 		} else if (sy->range_type == STYLE_RANGE_SESSION) {
272f844e94eSwiz 			snprintf(b, sizeof b, "session|$%u",
273f844e94eSwiz 			    sy->range_argument);
274f844e94eSwiz 			tmp = b;
275f844e94eSwiz 		} else if (sy->range_type == STYLE_RANGE_USER) {
276f844e94eSwiz 			snprintf(b, sizeof b, "user|%s", sy->range_string);
277f844e94eSwiz 			tmp = b;
278a66309acSwiz 		}
279ef36e747Schristos 		off += xsnprintf(s + off, sizeof s - off, "%srange=%s", comma,
280ef36e747Schristos 		    tmp);
281ef36e747Schristos 		comma = ",";
282ef36e747Schristos 	}
283ef36e747Schristos 	if (sy->align != STYLE_ALIGN_DEFAULT) {
284ef36e747Schristos 		if (sy->align == STYLE_ALIGN_LEFT)
285ef36e747Schristos 			tmp = "left";
286ef36e747Schristos 		else if (sy->align == STYLE_ALIGN_CENTRE)
287ef36e747Schristos 			tmp = "centre";
288ef36e747Schristos 		else if (sy->align == STYLE_ALIGN_RIGHT)
289ef36e747Schristos 			tmp = "right";
290e271dbb8Schristos 		else if (sy->align == STYLE_ALIGN_ABSOLUTE_CENTRE)
291e271dbb8Schristos 			tmp = "absolute-centre";
292ef36e747Schristos 		off += xsnprintf(s + off, sizeof s - off, "%salign=%s", comma,
293ef36e747Schristos 		    tmp);
294ef36e747Schristos 		comma = ",";
295ef36e747Schristos 	}
29668e6ba84Schristos 	if (sy->default_type != STYLE_DEFAULT_BASE) {
29768e6ba84Schristos 		if (sy->default_type == STYLE_DEFAULT_PUSH)
29868e6ba84Schristos 			tmp = "push-default";
29968e6ba84Schristos 		else if (sy->default_type == STYLE_DEFAULT_POP)
30068e6ba84Schristos 			tmp = "pop-default";
30168e6ba84Schristos 		off += xsnprintf(s + off, sizeof s - off, "%s%s", comma, tmp);
30268e6ba84Schristos 		comma = ",";
30368e6ba84Schristos 	}
30430744affSchristos 	if (sy->fill != 8) {
30530744affSchristos 		off += xsnprintf(s + off, sizeof s - off, "%sfill=%s", comma,
30630744affSchristos 		    colour_tostring(sy->fill));
30730744affSchristos 		comma = ",";
30830744affSchristos 	}
3094e179ddaSchristos 	if (gc->fg != 8) {
310ef36e747Schristos 		off += xsnprintf(s + off, sizeof s - off, "%sfg=%s", comma,
311ef36e747Schristos 		    colour_tostring(gc->fg));
312ef36e747Schristos 		comma = ",";
313928fc495Schristos 	}
3144e179ddaSchristos 	if (gc->bg != 8) {
315ef36e747Schristos 		off += xsnprintf(s + off, sizeof s - off, "%sbg=%s", comma,
316ef36e747Schristos 		    colour_tostring(gc->bg));
317ef36e747Schristos 		comma = ",";
318928fc495Schristos 	}
319f844e94eSwiz 	if (gc->us != 8) {
320f844e94eSwiz 		off += xsnprintf(s + off, sizeof s - off, "%sus=%s", comma,
321f844e94eSwiz 		    colour_tostring(gc->us));
322f844e94eSwiz 		comma = ",";
323f844e94eSwiz 	}
324e271dbb8Schristos 	if (gc->attr != 0) {
325ef36e747Schristos 		xsnprintf(s + off, sizeof s - off, "%s%s", comma,
326ef36e747Schristos 		    attributes_tostring(gc->attr));
327ef36e747Schristos 		comma = ",";
328928fc495Schristos 	}
329928fc495Schristos 
330928fc495Schristos 	if (*s == '\0')
331928fc495Schristos 		return ("default");
332928fc495Schristos 	return (s);
333928fc495Schristos }
334928fc495Schristos 
335e271dbb8Schristos /* Apply a style on top of the given style. */
336928fc495Schristos void
337e271dbb8Schristos style_add(struct grid_cell *gc, struct options *oo, const char *name,
338e271dbb8Schristos     struct format_tree *ft)
339928fc495Schristos {
340ef36e747Schristos 	struct style		*sy;
341e271dbb8Schristos 	struct format_tree	*ft0 = NULL;
342928fc495Schristos 
343e271dbb8Schristos 	if (ft == NULL)
344e271dbb8Schristos 		ft = ft0 = format_create(NULL, NULL, 0, FORMAT_NOJOBS);
345e271dbb8Schristos 
346e271dbb8Schristos 	sy = options_string_to_style(oo, name, ft);
347e271dbb8Schristos 	if (sy == NULL)
348e271dbb8Schristos 		sy = &style_default;
349e271dbb8Schristos 	if (sy->gc.fg != 8)
350ef36e747Schristos 		gc->fg = sy->gc.fg;
351e271dbb8Schristos 	if (sy->gc.bg != 8)
352ef36e747Schristos 		gc->bg = sy->gc.bg;
353f844e94eSwiz 	if (sy->gc.us != 8)
354f844e94eSwiz 		gc->us = sy->gc.us;
355ef36e747Schristos 	gc->attr |= sy->gc.attr;
356e271dbb8Schristos 
357e271dbb8Schristos 	if (ft0 != NULL)
358e271dbb8Schristos 		format_free(ft0);
359e271dbb8Schristos }
360e271dbb8Schristos 
361e271dbb8Schristos /* Apply a style on top of the default style. */
362e271dbb8Schristos void
363e271dbb8Schristos style_apply(struct grid_cell *gc, struct options *oo, const char *name,
364e271dbb8Schristos     struct format_tree *ft)
365e271dbb8Schristos {
366e271dbb8Schristos 	memcpy(gc, &grid_default_cell, sizeof *gc);
367e271dbb8Schristos 	style_add(gc, oo, name, ft);
368928fc495Schristos }
369928fc495Schristos 
370ef36e747Schristos /* Initialize style from cell. */
371ef36e747Schristos void
372ef36e747Schristos style_set(struct style *sy, const struct grid_cell *gc)
3735494e770Schristos {
374ef36e747Schristos 	memcpy(sy, &style_default, sizeof *sy);
375ef36e747Schristos 	memcpy(&sy->gc, gc, sizeof sy->gc);
376ef36e747Schristos }
377ef36e747Schristos 
378ef36e747Schristos /* Copy style. */
379ef36e747Schristos void
380ef36e747Schristos style_copy(struct style *dst, struct style *src)
381ef36e747Schristos {
382ef36e747Schristos 	memcpy(dst, src, sizeof *dst);
383ef36e747Schristos }
384