Home | History | Annotate | Line # | Download | only in dist
      1 /* $OpenBSD$ */
      2 
      3 /*
      4  * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott (at) gmail.com>
      5  * Copyright (c) 2014 Tiago Cunha <tcunha (at) 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 (~0)
     30 
     31 /* Default style. */
     32 static struct style style_default = {
     33 	{ { { ' ' }, 0, 1, 1 }, 0, 0, 8, 8, 0, 0 },
     34 	0,
     35 
     36 	8,
     37 	STYLE_ALIGN_DEFAULT,
     38 	STYLE_LIST_OFF,
     39 
     40 	STYLE_RANGE_NONE, 0, "",
     41 
     42 	STYLE_WIDTH_DEFAULT, STYLE_PAD_DEFAULT,
     43 
     44 	STYLE_DEFAULT_BASE
     45 };
     46 
     47 /* Set range string. */
     48 static void
     49 style_set_range_string(struct style *sy, const char *s)
     50 {
     51 	strlcpy(sy->range_string, s, sizeof sy->range_string);
     52 }
     53 
     54 /*
     55  * Parse an embedded style of the form "fg=colour,bg=colour,bright,...".  Note
     56  * that this adds onto the given style, so it must have been initialized
     57  * already.
     58  */
     59 int
     60 style_parse(struct style *sy, const struct grid_cell *base, const char *in)
     61 {
     62 	struct style	saved;
     63 	const char	delimiters[] = " ,\n", *errstr;
     64 	char		tmp[256], *found;
     65 	int		value;
     66 	size_t		end;
     67 	u_int		n;
     68 
     69 	if (*in == '\0')
     70 		return (0);
     71 	style_copy(&saved, sy);
     72 
     73 	log_debug("%s: %s", __func__, in);
     74 	do {
     75 		while (*in != '\0' && strchr(delimiters, *in) != NULL)
     76 			in++;
     77 		if (*in == '\0')
     78 			break;
     79 
     80 		end = strcspn(in, delimiters);
     81 		if (end > (sizeof tmp) - 1)
     82 			goto error;
     83 		memcpy(tmp, in, end);
     84 		tmp[end] = '\0';
     85 
     86 		log_debug("%s: %s", __func__, tmp);
     87 		if (strcasecmp(tmp, "default") == 0) {
     88 			sy->gc.fg = base->fg;
     89 			sy->gc.bg = base->bg;
     90 			sy->gc.us = base->us;
     91 			sy->gc.attr = base->attr;
     92 			sy->gc.flags = base->flags;
     93 		} else if (strcasecmp(tmp, "ignore") == 0)
     94 			sy->ignore = 1;
     95 		else if (strcasecmp(tmp, "noignore") == 0)
     96 			sy->ignore = 0;
     97 		else if (strcasecmp(tmp, "push-default") == 0)
     98 			sy->default_type = STYLE_DEFAULT_PUSH;
     99 		else if (strcasecmp(tmp, "pop-default") == 0)
    100 			sy->default_type = STYLE_DEFAULT_POP;
    101 		else if (strcasecmp(tmp, "set-default") == 0)
    102 			sy->default_type = STYLE_DEFAULT_SET;
    103 		else if (strcasecmp(tmp, "nolist") == 0)
    104 			sy->list = STYLE_LIST_OFF;
    105 		else if (strncasecmp(tmp, "list=", 5) == 0) {
    106 			if (strcasecmp(tmp + 5, "on") == 0)
    107 				sy->list = STYLE_LIST_ON;
    108 			else if (strcasecmp(tmp + 5, "focus") == 0)
    109 				sy->list = STYLE_LIST_FOCUS;
    110 			else if (strcasecmp(tmp + 5, "left-marker") == 0)
    111 				sy->list = STYLE_LIST_LEFT_MARKER;
    112 			else if (strcasecmp(tmp + 5, "right-marker") == 0)
    113 				sy->list = STYLE_LIST_RIGHT_MARKER;
    114 			else
    115 				goto error;
    116 		} else if (strcasecmp(tmp, "norange") == 0) {
    117 			sy->range_type = style_default.range_type;
    118 			sy->range_argument = style_default.range_type;
    119 			strlcpy(sy->range_string, style_default.range_string,
    120 			    sizeof sy->range_string);
    121 		} else if (end > 6 && strncasecmp(tmp, "range=", 6) == 0) {
    122 			found = strchr(tmp + 6, '|');
    123 			if (found != NULL) {
    124 				*found++ = '\0';
    125 				if (*found == '\0')
    126 					goto error;
    127 			}
    128 			if (strcasecmp(tmp + 6, "left") == 0) {
    129 				if (found != NULL)
    130 					goto error;
    131 				sy->range_type = STYLE_RANGE_LEFT;
    132 				sy->range_argument = 0;
    133 				style_set_range_string(sy, "");
    134 			} else if (strcasecmp(tmp + 6, "right") == 0) {
    135 				if (found != NULL)
    136 					goto error;
    137 				sy->range_type = STYLE_RANGE_RIGHT;
    138 				sy->range_argument = 0;
    139 				style_set_range_string(sy, "");
    140 			} else if (strcasecmp(tmp + 6, "pane") == 0) {
    141 				if (found == NULL)
    142 					goto error;
    143 				if (*found != '%' || found[1] == '\0')
    144 					goto error;
    145 				n = strtonum(found + 1, 0, UINT_MAX, &errstr);
    146 				if (errstr != NULL)
    147 					goto error;
    148 				sy->range_type = STYLE_RANGE_PANE;
    149 				sy->range_argument = n;
    150 				style_set_range_string(sy, "");
    151 			} else if (strcasecmp(tmp + 6, "window") == 0) {
    152 				if (found == NULL)
    153 					goto error;
    154 				n = strtonum(found, 0, UINT_MAX, &errstr);
    155 				if (errstr != NULL)
    156 					goto error;
    157 				sy->range_type = STYLE_RANGE_WINDOW;
    158 				sy->range_argument = n;
    159 				style_set_range_string(sy, "");
    160 			} else if (strcasecmp(tmp + 6, "session") == 0) {
    161 				if (found == NULL)
    162 					goto error;
    163 				if (*found != '$' || found[1] == '\0')
    164 					goto error;
    165 				n = strtonum(found + 1, 0, UINT_MAX, &errstr);
    166 				if (errstr != NULL)
    167 					goto error;
    168 				sy->range_type = STYLE_RANGE_SESSION;
    169 				sy->range_argument = n;
    170 				style_set_range_string(sy, "");
    171 			} else if (strcasecmp(tmp + 6, "user") == 0) {
    172 				if (found == NULL)
    173 					goto error;
    174 				sy->range_type = STYLE_RANGE_USER;
    175 				sy->range_argument = 0;
    176 				style_set_range_string(sy, found);
    177 			}
    178 		} else if (strcasecmp(tmp, "noalign") == 0)
    179 			sy->align = style_default.align;
    180 		else if (end > 6 && strncasecmp(tmp, "align=", 6) == 0) {
    181 			if (strcasecmp(tmp + 6, "left") == 0)
    182 				sy->align = STYLE_ALIGN_LEFT;
    183 			else if (strcasecmp(tmp + 6, "centre") == 0)
    184 				sy->align = STYLE_ALIGN_CENTRE;
    185 			else if (strcasecmp(tmp + 6, "right") == 0)
    186 				sy->align = STYLE_ALIGN_RIGHT;
    187 			else if (strcasecmp(tmp + 6, "absolute-centre") == 0)
    188 				sy->align = STYLE_ALIGN_ABSOLUTE_CENTRE;
    189 			else
    190 				goto error;
    191 		} else if (end > 5 && strncasecmp(tmp, "fill=", 5) == 0) {
    192 			if ((value = colour_fromstring(tmp + 5)) == -1)
    193 				goto error;
    194 			sy->fill = value;
    195 		} else if (end > 3 && strncasecmp(tmp + 1, "g=", 2) == 0) {
    196 			if ((value = colour_fromstring(tmp + 3)) == -1)
    197 				goto error;
    198 			if (*in == 'f' || *in == 'F') {
    199 				if (value != 8)
    200 					sy->gc.fg = value;
    201 				else
    202 					sy->gc.fg = base->fg;
    203 			} else if (*in == 'b' || *in == 'B') {
    204 				if (value != 8)
    205 					sy->gc.bg = value;
    206 				else
    207 					sy->gc.bg = base->bg;
    208 			} else
    209 				goto error;
    210 		} else if (end > 3 && strncasecmp(tmp, "us=", 3) == 0) {
    211 			if ((value = colour_fromstring(tmp + 3)) == -1)
    212 				goto error;
    213 			if (value != 8)
    214 				sy->gc.us = value;
    215 			else
    216 				sy->gc.us = base->us;
    217 		} else if (strcasecmp(tmp, "none") == 0)
    218 			sy->gc.attr = 0;
    219 		else if (end > 2 && strncasecmp(tmp, "no", 2) == 0) {
    220 			if (strcmp(tmp + 2, "attr") == 0)
    221 				sy->gc.attr |= GRID_ATTR_NOATTR;
    222 			else {
    223 				value = attributes_fromstring(tmp + 2);
    224 				if (value == -1)
    225 					goto error;
    226 				sy->gc.attr &= ~value;
    227 			}
    228 		} else if (end > 6 && strncasecmp(tmp, "width=", 6) == 0) {
    229 			n = strtonum(tmp + 6, 0, UINT_MAX, &errstr);
    230 			if (errstr != NULL)
    231 				goto error;
    232 			sy->width = (int)n;
    233 		} else if (end > 4 && strncasecmp(tmp, "pad=", 4) == 0) {
    234 			n = strtonum(tmp + 4, 0, UINT_MAX, &errstr);
    235 			if (errstr != NULL)
    236 				goto error;
    237 			sy->pad = (int)n;
    238 		} else {
    239 			if ((value = attributes_fromstring(tmp)) == -1)
    240 				goto error;
    241 			sy->gc.attr |= value;
    242 		}
    243 
    244 		in += end + strspn(in + end, delimiters);
    245 	} while (*in != '\0');
    246 
    247 	return (0);
    248 
    249 error:
    250 	style_copy(sy, &saved);
    251 	return (-1);
    252 }
    253 
    254 /* Convert style to a string. */
    255 const char *
    256 style_tostring(struct style *sy)
    257 {
    258 	struct grid_cell	*gc = &sy->gc;
    259 	int			 off = 0;
    260 	const char		*comma = "", *tmp = "";
    261 	static char		 s[256];
    262 	char			 b[21];
    263 
    264 	*s = '\0';
    265 
    266 	if (sy->list != STYLE_LIST_OFF) {
    267 		if (sy->list == STYLE_LIST_ON)
    268 			tmp = "on";
    269 		else if (sy->list == STYLE_LIST_FOCUS)
    270 			tmp = "focus";
    271 		else if (sy->list == STYLE_LIST_LEFT_MARKER)
    272 			tmp = "left-marker";
    273 		else if (sy->list == STYLE_LIST_RIGHT_MARKER)
    274 			tmp = "right-marker";
    275 		off += xsnprintf(s + off, sizeof s - off, "%slist=%s", comma,
    276 		    tmp);
    277 		comma = ",";
    278 	}
    279 	if (sy->range_type != STYLE_RANGE_NONE) {
    280 		if (sy->range_type == STYLE_RANGE_LEFT)
    281 			tmp = "left";
    282 		else if (sy->range_type == STYLE_RANGE_RIGHT)
    283 			tmp = "right";
    284 		else if (sy->range_type == STYLE_RANGE_PANE) {
    285 			snprintf(b, sizeof b, "pane|%%%u", sy->range_argument);
    286 			tmp = b;
    287 		} else if (sy->range_type == STYLE_RANGE_WINDOW) {
    288 			snprintf(b, sizeof b, "window|%u", sy->range_argument);
    289 			tmp = b;
    290 		} else if (sy->range_type == STYLE_RANGE_SESSION) {
    291 			snprintf(b, sizeof b, "session|$%u",
    292 			    sy->range_argument);
    293 			tmp = b;
    294 		} else if (sy->range_type == STYLE_RANGE_USER) {
    295 			snprintf(b, sizeof b, "user|%s", sy->range_string);
    296 			tmp = b;
    297 		}
    298 		off += xsnprintf(s + off, sizeof s - off, "%srange=%s", comma,
    299 		    tmp);
    300 		comma = ",";
    301 	}
    302 	if (sy->align != STYLE_ALIGN_DEFAULT) {
    303 		if (sy->align == STYLE_ALIGN_LEFT)
    304 			tmp = "left";
    305 		else if (sy->align == STYLE_ALIGN_CENTRE)
    306 			tmp = "centre";
    307 		else if (sy->align == STYLE_ALIGN_RIGHT)
    308 			tmp = "right";
    309 		else if (sy->align == STYLE_ALIGN_ABSOLUTE_CENTRE)
    310 			tmp = "absolute-centre";
    311 		off += xsnprintf(s + off, sizeof s - off, "%salign=%s", comma,
    312 		    tmp);
    313 		comma = ",";
    314 	}
    315 	if (sy->default_type != STYLE_DEFAULT_BASE) {
    316 		if (sy->default_type == STYLE_DEFAULT_PUSH)
    317 			tmp = "push-default";
    318 		else if (sy->default_type == STYLE_DEFAULT_POP)
    319 			tmp = "pop-default";
    320 		else if (sy->default_type == STYLE_DEFAULT_SET)
    321 			tmp = "set-default";
    322 		off += xsnprintf(s + off, sizeof s - off, "%s%s", comma, tmp);
    323 		comma = ",";
    324 	}
    325 	if (sy->fill != 8) {
    326 		off += xsnprintf(s + off, sizeof s - off, "%sfill=%s", comma,
    327 		    colour_tostring(sy->fill));
    328 		comma = ",";
    329 	}
    330 	if (gc->fg != 8) {
    331 		off += xsnprintf(s + off, sizeof s - off, "%sfg=%s", comma,
    332 		    colour_tostring(gc->fg));
    333 		comma = ",";
    334 	}
    335 	if (gc->bg != 8) {
    336 		off += xsnprintf(s + off, sizeof s - off, "%sbg=%s", comma,
    337 		    colour_tostring(gc->bg));
    338 		comma = ",";
    339 	}
    340 	if (gc->us != 8) {
    341 		off += xsnprintf(s + off, sizeof s - off, "%sus=%s", comma,
    342 		    colour_tostring(gc->us));
    343 		comma = ",";
    344 	}
    345 	if (gc->attr != 0) {
    346 		xsnprintf(s + off, sizeof s - off, "%s%s", comma,
    347 		    attributes_tostring(gc->attr));
    348 		comma = ",";
    349 	}
    350 	if (sy->width >= 0) {
    351 		xsnprintf(s + off, sizeof s - off, "%swidth=%u", comma,
    352 		    sy->width);
    353 		comma = ",";
    354 	}
    355 	if (sy->pad >= 0) {
    356 		xsnprintf(s + off, sizeof s - off, "%spad=%u", comma,
    357 		    sy->pad);
    358 		comma = ",";
    359 	}
    360 	if (*s == '\0')
    361 		return ("default");
    362 	return (s);
    363 }
    364 
    365 /* Apply a style on top of the given style. */
    366 void
    367 style_add(struct grid_cell *gc, struct options *oo, const char *name,
    368     struct format_tree *ft)
    369 {
    370 	struct style		*sy;
    371 	struct format_tree	*ft0 = NULL;
    372 
    373 	if (ft == NULL)
    374 		ft = ft0 = format_create(NULL, NULL, 0, FORMAT_NOJOBS);
    375 
    376 	sy = options_string_to_style(oo, name, ft);
    377 	if (sy == NULL)
    378 		sy = &style_default;
    379 	if (sy->gc.fg != 8)
    380 		gc->fg = sy->gc.fg;
    381 	if (sy->gc.bg != 8)
    382 		gc->bg = sy->gc.bg;
    383 	if (sy->gc.us != 8)
    384 		gc->us = sy->gc.us;
    385 	gc->attr |= sy->gc.attr;
    386 
    387 	if (ft0 != NULL)
    388 		format_free(ft0);
    389 }
    390 
    391 /* Apply a style on top of the default style. */
    392 void
    393 style_apply(struct grid_cell *gc, struct options *oo, const char *name,
    394     struct format_tree *ft)
    395 {
    396 	memcpy(gc, &grid_default_cell, sizeof *gc);
    397 	style_add(gc, oo, name, ft);
    398 }
    399 
    400 /* Initialize style from cell. */
    401 void
    402 style_set(struct style *sy, const struct grid_cell *gc)
    403 {
    404 	memcpy(sy, &style_default, sizeof *sy);
    405 	memcpy(&sy->gc, gc, sizeof sy->gc);
    406 }
    407 
    408 /* Copy style. */
    409 void
    410 style_copy(struct style *dst, struct style *src)
    411 {
    412 	memcpy(dst, src, sizeof *dst);
    413 }
    414 
    415 void
    416 style_set_scrollbar_style_from_option(struct style *sb_style, struct options *oo)
    417 {
    418 	struct style	*sy;
    419 
    420 	sy = options_string_to_style(oo, "pane-scrollbars-style", NULL);
    421 	if (sy == NULL) {
    422 		style_set(sb_style, &grid_default_cell);
    423 		sb_style->width = PANE_SCROLLBARS_DEFAULT_WIDTH;
    424 		sb_style->pad = PANE_SCROLLBARS_DEFAULT_PADDING;
    425 		utf8_set(&sb_style->gc.data, PANE_SCROLLBARS_CHARACTER);
    426 	} else {
    427 		style_copy(sb_style, sy);
    428 		if (sb_style->width < 1)
    429 			sb_style->width = PANE_SCROLLBARS_DEFAULT_WIDTH;
    430 		if (sb_style->pad < 0)
    431 			sb_style->pad = PANE_SCROLLBARS_DEFAULT_PADDING;
    432 		utf8_set(&sb_style->gc.data, PANE_SCROLLBARS_CHARACTER);
    433 	}
    434 }
    435