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