#if !defined(BINARY_TYFUZZ_COLOR_PARSER) #include "private.h" #include #include #include #include #include "termpty.h" #include "backlog.h" #include "termiolink.h" #include "termio.h" #include "sb.h" #include "utf8.h" #include "theme.h" #include "utils.h" #else #include #include #include #include #include #include #include #include "sb.h" #define eina_convert_strtod_c strtod #ifndef MIN # define MIN(x, y) (((x) > (y)) ? (y) : (x)) #endif #ifndef MAX # define MAX(x, y) (((x) > (y)) ? (x) : (y)) #endif typedef uint32_t Eina_Unicode; typedef bool Eina_Bool; #define EINA_TRUE true #define EINA_FALSE false #endif #if !defined(BINARY_TYFUZZ_COLOR_PARSER) __attribute__((const)) static Eina_Bool _isspace_unicode(const int codepoint) { switch (codepoint) { case 9: // character tabulation return EINA_TRUE; case 10: // line feed return EINA_TRUE; case 11: // line tabulation return EINA_TRUE; case 12: // form feed return EINA_TRUE; case 13: // carriage return return EINA_TRUE; case 32: // space return EINA_TRUE; case 133: // next line return EINA_TRUE; case 160: // no-break space return EINA_TRUE; case 5760: // ogham space mark return EINA_TRUE; case 6158: // mongolian vowel separator return EINA_TRUE; case 8192: // en quad return EINA_TRUE; case 8193: // em quad return EINA_TRUE; case 8194: // en space return EINA_TRUE; case 8195: // em space return EINA_TRUE; case 8196: // three-per-em space return EINA_TRUE; case 8197: // four-per-em space return EINA_TRUE; case 8198: // six-per-em space return EINA_TRUE; case 8199: // figure space return EINA_TRUE; case 8200: // puncturation space return EINA_TRUE; case 8201: // thin space return EINA_TRUE; case 8202: // hair space return EINA_TRUE; case 8203: // zero width space return EINA_TRUE; case 8204: // zero width non-joiner return EINA_TRUE; case 8205: // zero width joiner return EINA_TRUE; case 8232: // line separator return EINA_TRUE; case 8233: // paragraph separator return EINA_TRUE; case 8239: // narrow no-break space return EINA_TRUE; case 8287: // medium mathematical space return EINA_TRUE; case 8288: // word joiner return EINA_TRUE; case 12288: // ideographic space return EINA_TRUE; case 65279: // zero width non-breaking space return EINA_TRUE; } return EINA_FALSE; } static char * _cwd_path_get(const Evas_Object *obj, const char *relpath) { char cwdpath[PATH_MAX], tmppath[PATH_MAX]; if (!termio_cwd_get(obj, cwdpath, sizeof(cwdpath))) return NULL; eina_str_join(tmppath, sizeof(tmppath), '/', cwdpath, relpath); return strdup(tmppath); } static char * _home_path_get(const Evas_Object *_obj EINA_UNUSED, const char *relpath) { char tmppath[PATH_MAX], homepath[PATH_MAX]; if (!homedir_get(homepath, sizeof(homepath))) return NULL; eina_str_join(tmppath, sizeof(tmppath), '/', homepath, relpath); return strdup(tmppath); } static char * _local_path_get(const Evas_Object *obj, const char *relpath) { if (relpath[0] == '/') return strdup(relpath); else if (eina_str_has_prefix(relpath, "~/")) return _home_path_get(obj, relpath + 2); else return _cwd_path_get(obj, relpath); } /* isalpha() may produce unsigned-integer-overflow * runtime error: unsigned integer overflow: 32 - 97 cannot be represented in * type 'unsigned int' * int isalpha(int c) * { * return ((unsigned)c|32)-'a' < 26; * } */ #if defined(__clang__) __attribute__((no_sanitize("unsigned-integer-overflow"))) #endif Eina_Bool link_is_protocol(const char *str) { const char *p = str; int c; if (!str) return EINA_FALSE; c = *p; if (!isalpha(c)) return EINA_FALSE; /* Try to follow RFC3986 a bit * URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] * hier-part = "//" authority path-abempty * [...] other stuff not taken into account * scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) */ do { p++; c = *p; } while (isalpha(c) || (c == '.') || (c == '-') || (c == '+')); return (p[0] == ':') && (p[1] == '/') && (p[2] == '/'); } Eina_Bool link_is_url(const char *str) { if (!str) return EINA_FALSE; if (link_is_protocol(str) || casestartswith(str, "www.") || casestartswith(str, "ftp.")) return EINA_TRUE; return EINA_FALSE; } Eina_Bool link_is_email(const char *str) { const char *at; if (!str) return EINA_FALSE; at = strchr(str, '@'); if (at && strchr(at + 1, '.')) return EINA_TRUE; if (casestartswith(str, "mailto:")) return EINA_TRUE; return EINA_FALSE; } Eina_Bool link_is_file(const char *str) { if (!str) return EINA_FALSE; switch (str[0]) { case '/': return EINA_TRUE; case '~': if (str[1] == '/') return EINA_TRUE; return EINA_FALSE; case '.': if (str[1] == '/') return EINA_TRUE; else if ((str[1] == '.') && (str[2] == '/')) return EINA_TRUE; return EINA_FALSE; default: return EINA_FALSE; } } static int _txt_at(Termpty *ty, int *x, int *y, char *txt, int *txtlenp, int *codepointp) { Termcell *cells = NULL; Termcell cell; ssize_t w; cells = termpty_cellrow_get(ty, *y, &w); if (!cells || !w) goto bad; if ((*x >= w)) goto empty; cell = cells[*x]; if ((cell.codepoint == 0) && (cell.att.dblwidth)) { (*x)--; if (*x < 0) goto bad; cell = cells[*x]; } if (cell.att.tab_inserted) { *txtlenp = 1; *codepointp = '\t'; txt[0] = '\t'; return 0; } if ((cell.codepoint == 0) || (cell.att.link_id)) goto empty; *txtlenp = codepoint_to_utf8(cell.codepoint, txt); *codepointp = cell.codepoint; return 0; empty: txt[0] = '\0'; *txtlenp = 0; *codepointp = 0; return 0; bad: *txtlenp = 0; txt[0] = '\0'; return -1; } static int _txt_prev_at(Termpty *ty, int *x, int *y, char *txt, int *txtlenp, int *codepointp) { Termcell *cells = NULL; Termcell cell; ssize_t w; (*x)--; if ((*x) < 0) { (*y)--; *x = ty->w-1; cells = termpty_cellrow_get(ty, *y, &w); if (!cells || !w) goto bad; if ((*x) >= w) goto empty; cell = cells[*x]; /* Either the cell is in the normal screen and needs to have * autowrapped flag or is in the backlog and its length is larger than * the screen, spanning multiple lines */ if (((!cell.att.autowrapped) && (*y) >= 0) || (w < ty->w)) goto empty; } else { cells = termpty_cellrow_get(ty, *y, &w); if (!cells || !w) goto bad; if ((*x) >= w) goto empty; cell = cells[*x]; } if ((cell.codepoint == 0) && (cell.att.dblwidth)) { (*x)--; if (*x < 0) goto bad; cell = cells[*x]; } if (cell.att.tab_last) { while (*x >= 0 && !cells[*x].att.tab_inserted) (*x)--; if (*x < 0) goto bad; *txtlenp = 1; *codepointp = '\t'; txt[0] = '\t'; return 0; } if ((cell.codepoint == 0) || (cell.att.link_id)) goto empty; *txtlenp = codepoint_to_utf8(cell.codepoint, txt); *codepointp = cell.codepoint; return 0; empty: txt[0] = '\0'; *txtlenp = 0; *codepointp = 0; return 0; bad: txt[0] = '\0'; *txtlenp = 0; return -1; } static int _txt_next_at(Termpty *ty, int *x, int *y, char *txt, int *txtlenp, int *codepointp) { Termcell *cells = NULL; Termcell cell; ssize_t w; (*x)++; cells = termpty_cellrow_get(ty, *y, &w); if (!cells || !w) goto bad; if ((*x) >= w) { (*y)++; if ((*x) <= ty->w) { cell = cells[w-1]; if (!cell.att.autowrapped) goto empty; } *x = 0; cells = termpty_cellrow_get(ty, *y, &w); if (!cells || !w) goto bad; } cell = cells[*x]; if ((cell.codepoint == 0) && (cell.att.dblwidth)) { (*x)++; if (*x >= w) { cell = cells[w-1]; if (!cell.att.autowrapped && w == ty->w) goto empty; (*y)++; *x = 0; cells = termpty_cellrow_get(ty, *y, &w); if (!cells || !w) goto bad; } goto bad; } cell = cells[*x]; if (cell.att.tab_inserted) { while (*x < w && !cells[*x].att.tab_last) (*x)++; if (*x >= w) goto bad; *txtlenp = 1; *codepointp = '\t'; txt[0] = '\t'; return 0; } if ((cell.codepoint == 0) || (cell.att.link_id)) goto empty; *txtlenp = codepoint_to_utf8(cell.codepoint, txt); *codepointp = cell.codepoint; return 0; empty: txt[0] = '\0'; *txtlenp = 0; *codepointp = 0; return 0; bad: *txtlenp = 0; txt[0] = '\0'; return -1; } /* returned string must be freed */ char * termio_link_find(const Evas_Object *obj, int cx, int cy, int *x1r, int *y1r, int *x2r, int *y2r) { char *s = NULL; int endmatch1 = 0, endmatch2 = 0; int x1, x2, y1, y2, w = 0, h = 0, sc; Eina_Bool goback = EINA_TRUE, goforward = EINA_FALSE, escaped = EINA_FALSE; struct ty_sb sb = {.buf = NULL, .gap = 0, .len = 0, .alloc = 0}; Termpty *ty = termio_pty_get(obj); int res; char txt[8]; int txtlen = 0; int codepoint = 0; Eina_Bool was_protocol = EINA_FALSE; EINA_SAFETY_ON_NULL_RETURN_VAL(ty, NULL); x1 = x2 = cx; y1 = y2 = cy; termio_size_get(obj, &w, &h); if ((w <= 0) || (h <= 0)) goto end; sc = termio_scroll_get(obj); termpty_backlog_lock(); y1 -= sc; y2 -= sc; res = _txt_at(ty, &x1, &y1, txt, &txtlen, &codepoint); if ((res != 0) || (txtlen == 0)) goto end; if (_isspace_unicode(codepoint)) goto end; res = ty_sb_add(&sb, txt, txtlen); if (res < 0) goto end; while (goback) { int new_x1 = x1, new_y1 = y1; res = _txt_prev_at(ty, &new_x1, &new_y1, txt, &txtlen, &codepoint); if ((res != 0) || (txtlen == 0)) { goback = EINA_FALSE; goforward = EINA_TRUE; break; } res = ty_sb_prepend(&sb, txt, txtlen); if (res < 0) goto end; if (_isspace_unicode(codepoint)) { int old_txtlen = txtlen; res = _txt_prev_at(ty, &new_x1, &new_y1, txt, &txtlen, &codepoint); if ((res != 0) || (txtlen == 0) || (codepoint != '\\')) { ty_sb_lskip(&sb, old_txtlen); goback = EINA_FALSE; goforward = EINA_TRUE; break; } } switch (codepoint) { case '"': endmatch1 = endmatch2 = '"'; break; case '\'': endmatch1 = endmatch2 = '\''; break; case '`': endmatch1 = endmatch2 = '`'; break; case '<': endmatch1 = endmatch2 = '>'; break; case '[': endmatch1 = endmatch2 = ']'; break; case ']': endmatch1 = endmatch2 = '['; break; case '{': endmatch1 = endmatch2 = '}'; break; case '(': endmatch1 = endmatch2 = ')'; break; case '|': endmatch1 = endmatch2 = '|'; break; case 0xab: endmatch1 = endmatch2 = 0xbb; break; // french « » case 0xbb: endmatch1 = endmatch2 = 0xab; break; // swedish » « case 0x2018: endmatch1 = endmatch2 = 0x2019; break; // ‘ ’ case 0x201b: endmatch1 = endmatch2 = 0x2019; break; // ‛ ’ case 0x201c: endmatch1 = endmatch2 = 0x201d; break; // “ ” case 0x201e: endmatch1 = 0x201c; endmatch2 = 0x201d; break; // „ “” case 0x2039: endmatch1 = endmatch2 = 0x203a; break; // ‹› case 0x27e6: endmatch1 = endmatch2 = 0x27e7; break; // ⟦ ⟧ case 0x27e8: endmatch1 = endmatch2 = 0x27e9; break; // ⟨ ⟩ case 0x2329: endmatch1 = endmatch2 = 0x232a; break; // 〈 〉 case 0x231c: endmatch1 = 0x231d; endmatch2 = 0x231f; break; // ⌜⌝⌟ case 0x231e: endmatch1 = 0x231d; endmatch2 = 0x231f; break; // ⌞⌝⌟ case 0x2308: endmatch1 = 0x2309; endmatch2 = 0x230b; break; // ⌈⌉⌋ case 0x230a: endmatch1 = 0x2309; endmatch2 = 0x230b; break; // ⌊⌉⌋ } if (endmatch1) { ty_sb_lskip(&sb, txtlen); goback = EINA_FALSE; goforward = EINA_TRUE; break; } if (!link_is_protocol(sb.buf)) { if (was_protocol) { if (!_isspace_unicode(codepoint)) endmatch1 = endmatch2 = codepoint; ty_sb_lskip(&sb, txtlen); goback = EINA_FALSE; goforward = EINA_TRUE; break; } } else { was_protocol = EINA_TRUE; } x1 = new_x1; y1 = new_y1; } while (goforward) { int new_x2 = x2, new_y2 = y2; /* Check if the previous char is a delimiter */ res = _txt_next_at(ty, &new_x2, &new_y2, txt, &txtlen, &codepoint); if ((res != 0) || (txtlen == 0)) { goforward = EINA_FALSE; break; } /* escaping */ if (codepoint == '\\') { x2 = new_x2; y2 = new_y2; escaped = EINA_TRUE; continue; } if (escaped) { x2 = new_x2; y2 = new_y2; escaped = EINA_FALSE; } if (_isspace_unicode(codepoint) || (codepoint == endmatch1) || (codepoint == endmatch2)) { goforward = EINA_FALSE; break; } switch (codepoint) { case '"': goto out; case '\'': goto out; case '`': goto out; case '<': goto out; case '>': goto out; case '[': goto out; case ']': goto out; case '{': goto out; case '}': goto out; case '|': goto out; case 0xab: goto out; case 0xbb: goto out; case 0x2018: goto out; case 0x2019: goto out; case 0x201b: goto out; case 0x201c: goto out; case 0x201d: goto out; case 0x201e: goto out; case 0x2039: goto out; case 0x203a: goto out; case 0x2308: goto out; case 0x2309: goto out; case 0x230a: goto out; case 0x230b: goto out; case 0x231c: goto out; case 0x231d: goto out; case 0x231e: goto out; case 0x231f: goto out; case 0x2329: goto out; case 0x232a: goto out; case 0x27e6: goto out; case 0x27e7: goto out; case 0x27e8: goto out; case 0x27e9: goto out; } res = ty_sb_add(&sb, txt, txtlen); if (res < 0) goto end; if (!link_is_protocol(sb.buf)) { if (was_protocol) { ty_sb_rskip(&sb, txtlen); goback = EINA_FALSE; } } else { was_protocol = EINA_TRUE; } x2 = new_x2; y2 = new_y2; } out: if (sb.len) { Eina_Bool is_file = link_is_file(sb.buf); if (is_file || link_is_email(sb.buf) || link_is_url(sb.buf)) { if (x1r) *x1r = x1; if (y1r) *y1r = y1 + sc; if (x2r) *x2r = x2; if (y2r) *y2r = y2 + sc; if (is_file && (sb.buf[0] != '/')) { char *local= _local_path_get(obj, (const char*)sb.buf); ty_sb_free(&sb); s = local; goto end; } else { s = ty_sb_steal_buf(&sb); } goto end; } } end: termpty_backlog_unlock(); ty_sb_free(&sb); return s; } #endif __attribute__((const)) static Eina_Bool _is_authorized_in_color(const int codepoint) { switch (codepoint) { case '\t': return EINA_TRUE; case ' ': return EINA_TRUE; case '#': return EINA_TRUE; case '%': return EINA_TRUE; case '(': return EINA_TRUE; case '+': return EINA_TRUE; case ',': return EINA_TRUE; case '.': return EINA_TRUE; case '/': return EINA_TRUE; case '0': return EINA_TRUE; case '1': return EINA_TRUE; case '2': return EINA_TRUE; case '3': return EINA_TRUE; case '4': return EINA_TRUE; case '5': return EINA_TRUE; case '6': return EINA_TRUE; case '7': return EINA_TRUE; case '8': return EINA_TRUE; case '9': return EINA_TRUE; case ':': return EINA_TRUE; case 'A': return EINA_TRUE; case 'B': return EINA_TRUE; case 'C': return EINA_TRUE; case 'D': return EINA_TRUE; case 'E': return EINA_TRUE; case 'F': return EINA_TRUE; case 'a': return EINA_TRUE; case 'b': return EINA_TRUE; case 'c': return EINA_TRUE; case 'd': return EINA_TRUE; case 'e': return EINA_TRUE; case 'f': return EINA_TRUE; case 'g': return EINA_TRUE; case 'h': return EINA_TRUE; case 'l': return EINA_TRUE; case 'n': return EINA_TRUE; case 'o': return EINA_TRUE; case 'r': return EINA_TRUE; case 's': return EINA_TRUE; case 't': return EINA_TRUE; case 'u': return EINA_TRUE; } return EINA_FALSE; } static Eina_Bool _parse_hex(const char c, uint8_t *v) { if (c < '0') return EINA_FALSE; if (c <= '9') { *v = c - '0'; return EINA_TRUE; } if (c < 'A') return EINA_FALSE; if (c <= 'F') { *v = 10 + c - 'A'; return EINA_TRUE; } if (c < 'a') return EINA_FALSE; if (c <= 'f') { *v = 10 + c - 'a'; return EINA_TRUE; } return EINA_FALSE; } static Eina_Bool _parse_2hex(const char *s, uint8_t *v) { uint8_t v0, v1; if (!_parse_hex(s[0], &v0)) return EINA_FALSE; if (!_parse_hex(s[1], &v1)) return EINA_FALSE; *v = v0 << 4 | v1; return EINA_TRUE; } static Eina_Bool _parse_sharp_color(struct ty_sb *sb, uint8_t *rp, uint8_t *gp, uint8_t *bp, uint8_t *ap) { uint8_t r, g, b, a = 255; /* sharp */ if (sb->buf[0] != '#') return EINA_FALSE; ty_sb_lskip(sb, 1); switch (sb->len) { case 3: if ((!_parse_hex(sb->buf[0], &r)) || (!_parse_hex(sb->buf[1], &g)) || (!_parse_hex(sb->buf[2], &b))) return EINA_FALSE; r <<= 4; g <<= 4; b <<= 4; break; case 4: if ((!_parse_hex(sb->buf[0], &r)) || (!_parse_hex(sb->buf[1], &g)) || (!_parse_hex(sb->buf[2], &b)) || (!_parse_hex(sb->buf[3], &a))) return EINA_FALSE; r <<= 4; g <<= 4; b <<= 4; a <<= 4; break; case 6: if ((!_parse_2hex(&sb->buf[0], &r)) || (!_parse_2hex(&sb->buf[2], &g)) || (!_parse_2hex(&sb->buf[4], &b))) return EINA_FALSE; break; case 8: if ((!_parse_2hex(&sb->buf[0], &r)) || (!_parse_2hex(&sb->buf[2], &g)) || (!_parse_2hex(&sb->buf[4], &b)) || (!_parse_2hex(&sb->buf[6], &a))) return EINA_FALSE; break; default: return EINA_FALSE; } *rp = r; *gp = g; *bp = b; *ap = a; return EINA_TRUE; } static Eina_Bool _parse_uint8(struct ty_sb *sb, uint8_t *vp) { uint16_t v = 0; Eina_Bool ret = EINA_FALSE; if (!sb->len) return EINA_FALSE; while (sb->len && v < 255 && sb->buf[0] >= '0' && sb->buf[0] <= '9') { v = (v * 10) + sb->buf[0] - '0'; ty_sb_lskip(sb, 1); ret = EINA_TRUE; } if (v > 255) return EINA_FALSE; *vp = v; return ret; } /* isnan() in musl generates ' runtime error: negation of 1 cannot be * represented in type 'unsigned long long' * under ubsan */ #if defined(__clang__) && !defined(__GLIBC__) __attribute__((no_sanitize("unsigned-integer-overflow"))) #endif static Eina_Bool _parse_one_css_rgb_color(struct ty_sb *sb, uint8_t *vp, Eina_Bool *is_percentp) { char *endptr_double, *endptr_long; double d; long int l; if (!sb->len) return EINA_FALSE; d = eina_convert_strtod_c(sb->buf, &endptr_double); l = (long int)strtol(sb->buf, &endptr_long, 0); if (endptr_double == sb->buf || d < 0 || l < 0 || isnan(d)) return EINA_FALSE; if (endptr_double > endptr_long) { ty_sb_lskip(sb, endptr_double - sb->buf); *is_percentp = sb->len && sb->buf[0] == '%'; if (*is_percentp) { ty_sb_lskip(sb, 1); if (d > 100.0) return EINA_FALSE; d = (255.0 * d) / 100; } if (d > 255) return EINA_FALSE; *vp = round(d); } else { ty_sb_lskip(sb, endptr_long - sb->buf); *is_percentp = sb->len && sb->buf[0] == '%'; if (*is_percentp) { ty_sb_lskip(sb, 1); if (l > 100) return EINA_FALSE; l = (255 * l) / 100; } if (l > 255) return EINA_FALSE; *vp = (uint8_t)l; } return EINA_TRUE; } /* isnan() in musl generates ' runtime error: negation of 1 cannot be * represented in type 'unsigned long long' * under ubsan */ #if defined(__clang__) && !defined(__GLIBC__) __attribute__((no_sanitize("unsigned-integer-overflow"))) #endif static Eina_Bool _parse_one_css_alpha(struct ty_sb *sb, uint8_t *ap) { char *endptr_double; double d; if (!sb->len) return EINA_FALSE; d = eina_convert_strtod_c(sb->buf, &endptr_double); if (endptr_double == sb->buf || d < 0 || isnan(d)) return EINA_FALSE; ty_sb_lskip(sb, endptr_double - sb->buf); if (sb->len && sb->buf[0] == '%') { ty_sb_lskip(sb, 1); if (d > 100.0) return EINA_FALSE; d = (255.0 * d) / 100; } else { d *= 255.0; } if (d > 255) return EINA_FALSE; *ap = round(d); return EINA_TRUE; } /* isnan() in musl generates ' runtime error: negation of 1 cannot be * represented in type 'unsigned long long' * under ubsan */ #if defined(__clang__) && !defined(__GLIBC__) __attribute__((no_sanitize("unsigned-integer-overflow"))) #endif /* returns hue between 0 and 1 */ static Eina_Bool _parse_one_hue(struct ty_sb *sb, double *dp) { char *endptr_double; double d; if (!sb->len) return EINA_FALSE; d = eina_convert_strtod_c(sb->buf, &endptr_double); if (endptr_double == sb->buf || d < 0 || isnan(d)) return EINA_FALSE; ty_sb_lskip(sb, endptr_double - sb->buf); if (sbstartswith(sb, "turn")) { ty_sb_lskip(sb, strlen("turn")); } else if (sbstartswith(sb, "rad")) { ty_sb_lskip(sb, strlen("rad")); d /= 2 * M_PI; } else if (sbstartswith(sb, "grad")) { ty_sb_lskip(sb, strlen("grad")); d /= 400; } else if (sbstartswith(sb, "deg") || sb->buf[0] == ',' || sb->buf[0] == ' ') { if (sbstartswith(sb, "deg")) ty_sb_lskip(sb, strlen("deg")); d /= 360; } else return EINA_FALSE; if (d >= (double)LONG_MAX) return EINA_FALSE; d = d - (long) d; if (d < 0) d = 1 + d; *dp = d; return EINA_TRUE; } /* isnan() in musl generates ' runtime error: negation of 1 cannot be * represented in type 'unsigned long long' * under ubsan */ #if defined(__clang__) && !defined(__GLIBC__) __attribute__((no_sanitize("unsigned-integer-overflow"))) #endif static Eina_Bool _parse_one_percent(struct ty_sb *sb, double *dp) { char *endptr_double; double d; if (!sb->len) return EINA_FALSE; d = eina_convert_strtod_c(sb->buf, &endptr_double); if (isnan(d) || endptr_double == sb->buf || d < 0) return EINA_FALSE; ty_sb_lskip(sb, endptr_double - sb->buf); if (!sb->len || sb->buf[0] != '%') return EINA_FALSE; ty_sb_lskip(sb, 1); if (d > 100.0) return EINA_FALSE; d /= 100.0; *dp = d; return EINA_TRUE; } /* From http://en.wikipedia.org/wiki/HSL_color_space */ static Eina_Bool _hsl_to_rgb(double hue, double saturation, double lightness, uint8_t *rp, uint8_t *gp, uint8_t *bp) { double a = saturation * MIN(lightness, 1.0 - lightness); double n[3] = {0., 8., 4.}; double res[3] = {}; int i; for (i = 0; i < 3; i++) { double k = fmod(n[i] + 12.0 * hue, 12.); double f = lightness - a * MAX(-1, MIN(MIN(k - 3, 9 - k), 1)); if (f > 1 || f < 0) return EINA_FALSE; res[i] = f * 255.0; } *rp = res[0]; *gp = res[1]; *bp = res[2]; return EINA_TRUE; } static Eina_Bool _parse_css_hsl_color(struct ty_sb *sb, uint8_t *rp, uint8_t *gp, uint8_t *bp, uint8_t *ap) { uint8_t r = 0, g = 0, b = 0, a = 255; double hue, saturation, lightness; Eina_Bool is_functional; ty_sb_spaces_ltrim(sb); if (!sbstartswith(sb, "hsl")) return EINA_FALSE; ty_sb_lskip(sb, 3); if (sb->buf[0] == 'a') ty_sb_lskip(sb, 1); if (!sb->len) return EINA_FALSE; ty_sb_spaces_ltrim(sb); if (!sb->len || sb->buf[0] != '(') return EINA_FALSE; ty_sb_lskip(sb, 1); if (!sb->len) return EINA_FALSE; ty_sb_spaces_ltrim(sb); if (!_parse_one_hue(sb, &hue)) return EINA_FALSE; is_functional = (sb->len && sb->buf[0] == ','); if (is_functional) { ty_sb_lskip(sb, 1); } ty_sb_spaces_ltrim(sb); if (!sb->len) return EINA_FALSE; if (!_parse_one_percent(sb, &saturation)) return EINA_FALSE; ty_sb_spaces_ltrim(sb); if (!sb->len) return EINA_FALSE; if (is_functional != (sb->buf[0] == ',')) return EINA_FALSE; if (is_functional) ty_sb_lskip(sb, 1); ty_sb_spaces_ltrim(sb); if (!sb->len) return EINA_FALSE; if (!_parse_one_percent(sb, &lightness)) return EINA_FALSE; ty_sb_spaces_ltrim(sb); if (!sb->len) return EINA_FALSE; if (sb->buf[0] == ')') { ty_sb_lskip(sb, 1); a = 255; } else { if ((is_functional && (sb->buf[0] != ',')) || (!is_functional && (sb->buf[0] != '/'))) return EINA_FALSE; if (!sb->len) return EINA_FALSE; ty_sb_lskip(sb, 1); ty_sb_spaces_ltrim(sb); if (!sb->len) return EINA_FALSE; if (!_parse_one_css_alpha(sb, &a)) return EINA_FALSE; ty_sb_spaces_ltrim(sb); if (sb->buf[0] != ')') return EINA_FALSE; } if (!_hsl_to_rgb(hue, saturation, lightness, &r, &g, &b)) return EINA_FALSE; *rp = r; *gp = g; *bp = b; *ap = a; return EINA_TRUE; } static Eina_Bool _parse_css_rgb_color(struct ty_sb *sb, uint8_t *rp, uint8_t *gp, uint8_t *bp, uint8_t *ap) { uint8_t r = 0, g = 0, b = 0, a = 255; Eina_Bool must_be_percent, is_percent, is_functional; ty_sb_spaces_ltrim(sb); if (!sbstartswith(sb, "rgb")) return EINA_FALSE; ty_sb_lskip(sb, 3); if (sb->buf[0] == 'a') ty_sb_lskip(sb, 1); if (!sb->len) return EINA_FALSE; ty_sb_spaces_ltrim(sb); if (!sb->len || sb->buf[0] != '(') return EINA_FALSE; ty_sb_lskip(sb, 1); if (!sb->len) return EINA_FALSE; ty_sb_spaces_ltrim(sb); if (!_parse_one_css_rgb_color(sb, &r, &must_be_percent)) return EINA_FALSE; ty_sb_spaces_ltrim(sb); is_functional = (sb->buf[0] == ','); if (is_functional) ty_sb_lskip(sb, 1); ty_sb_spaces_ltrim(sb); if (!sb->len) return EINA_FALSE; if (!_parse_one_css_rgb_color(sb, &g, &is_percent)) return EINA_FALSE; if (is_percent != must_be_percent) return EINA_FALSE; ty_sb_spaces_ltrim(sb); if (is_functional != (sb->buf[0] == ',')) return EINA_FALSE; if (is_functional) ty_sb_lskip(sb, 1); ty_sb_spaces_ltrim(sb); if (!sb->len) return EINA_FALSE; if (!_parse_one_css_rgb_color(sb, &b, &is_percent)) return EINA_FALSE; if (is_percent != must_be_percent) return EINA_FALSE; ty_sb_spaces_ltrim(sb); if (sb->buf[0] == ')') { ty_sb_lskip(sb, 1); a = 255; } else { if ((is_functional && (sb->buf[0] != ',')) || (!is_functional && (sb->buf[0] != '/'))) return EINA_FALSE; if (!sb->len) return EINA_FALSE; ty_sb_lskip(sb, 1); ty_sb_spaces_ltrim(sb); if (!sb->len) return EINA_FALSE; if (!_parse_one_css_alpha(sb, &a)) return EINA_FALSE; ty_sb_spaces_ltrim(sb); if (sb->buf[0] != ')') return EINA_FALSE; } *rp = r; *gp = g; *bp = b; *ap = a; return EINA_TRUE; } static Eina_Bool _parse_edc_color(struct ty_sb *sb, uint8_t *rp, uint8_t *gp, uint8_t *bp, uint8_t *ap) { uint8_t r = 0, g = 0, b = 0, a = 255; ty_sb_spaces_ltrim(sb); /* skip color */ if (sb->len <= 6) return EINA_FALSE; if (strncmp(sb->buf, "color", 5) != 0) return EINA_FALSE; ty_sb_lskip(sb, 5); if (sb->buf[0] == '2' || sb->buf[0] == '3') ty_sb_lskip(sb, 1); ty_sb_spaces_ltrim(sb); if (sb->buf[0] != ':') return EINA_FALSE; ty_sb_lskip(sb, 1); /* skip ':' */ ty_sb_spaces_ltrim(sb); if (!sb->len) return EINA_FALSE; if (sb->buf[0] == '#') return _parse_sharp_color(sb, rp, gp, bp, ap); if (sbstartswith(sb, "rgb")) return _parse_css_rgb_color(sb, rp, gp, bp, ap); if (sbstartswith(sb, "hsl")) return _parse_css_hsl_color(sb, rp, gp, bp, ap); if (!_parse_uint8(sb, &r)) return EINA_FALSE; ty_sb_spaces_ltrim(sb); if (!_parse_uint8(sb, &g)) return EINA_FALSE; ty_sb_spaces_ltrim(sb); if (!_parse_uint8(sb, &b)) return EINA_FALSE; ty_sb_spaces_ltrim(sb); if (!_parse_uint8(sb, &a)) return EINA_FALSE; *rp = r; *gp = g; *bp = b; *ap = a; return EINA_TRUE; } __attribute__((const)) static Eina_Bool _is_authorized_in_color_sharp(const Eina_Unicode g) { if (g == '#' || (g >= '0' && g <= '9') || (g >= 'A' && g <= 'F') || (g >= 'a' && g <= 'f')) return EINA_TRUE; return EINA_FALSE; } static Eina_Bool _parse_color(struct ty_sb *sb, uint8_t *r, uint8_t *g, uint8_t *b, uint8_t *a) { if (!sb->len) goto end; if (sb->buf[0] == '#') { if (!_parse_sharp_color(sb, r, g, b, a)) goto end; } else if (sbstartswith(sb, "color")) { if (!_parse_edc_color(sb, r, g, b, a)) goto end; } else if (sbstartswith(sb, "rgb")) { if (!_parse_css_rgb_color(sb, r, g, b, a)) goto end; } else if (sbstartswith(sb, "hsl")) { if (!_parse_css_hsl_color(sb, r, g, b, a)) goto end; } else goto end; return EINA_TRUE; end: return EINA_FALSE; } #if !defined(BINARY_TYFUZZ_COLOR_PARSER) static Eina_Bool _sharp_color_find(Termpty *ty, int sc, int *x1r, int *y1r, int *x2r, int *y2r, uint8_t *rp, uint8_t *gp, uint8_t *bp, uint8_t *ap) { int x1 = *x1r, y1 = *y1r, x2 = *x2r, y2 = *y2r; Eina_Bool goback = EINA_TRUE, goforward = EINA_FALSE; struct ty_sb sb = {.buf = NULL, .gap = 0, .len = 0, .alloc = 0}; int res; char txt[8]; int txtlen = 0; Eina_Bool found = EINA_FALSE; int codepoint; uint8_t r, g, b, a = 255; res = _txt_at(ty, &x1, &y1, txt, &txtlen, &codepoint); if ((res != 0) || (txtlen == 0)) goto end; if (!_is_authorized_in_color_sharp(codepoint)) goto end; res = ty_sb_add(&sb, txt, txtlen); if (res < 0) goto end; while (goback && sb.len <= 9) { int new_x1 = x1, new_y1 = y1; res = _txt_prev_at(ty, &new_x1, &new_y1, txt, &txtlen, &codepoint); if ((res != 0) || (txtlen == 0)) { goback = EINA_FALSE; goforward = EINA_TRUE; break; } res = ty_sb_prepend(&sb, txt, txtlen); if (res < 0) goto end; if (!_is_authorized_in_color_sharp(codepoint)) { ty_sb_lskip(&sb, txtlen); goback = EINA_FALSE; goforward = EINA_TRUE; break; } x1 = new_x1; y1 = new_y1; } while (goforward && sb.len <= 9) { int new_x2 = x2, new_y2 = y2; /* Check if the previous char is a delimiter */ res = _txt_next_at(ty, &new_x2, &new_y2, txt, &txtlen, &codepoint); if ((res != 0) || (txtlen == 0)) { goforward = EINA_FALSE; break; } if (!_is_authorized_in_color_sharp(codepoint)) { goforward = EINA_FALSE; break; } res = ty_sb_add(&sb, txt, txtlen); if (res < 0) goto end; x2 = new_x2; y2 = new_y2; } if (sb.buf[0] == '#') { if (!_parse_sharp_color(&sb, &r, &g, &b, &a)) goto end; found = EINA_TRUE; } end: ty_sb_free(&sb); if (found) { *rp = r; *gp = g; *bp = b; *ap = a; *x1r = x1; *y1r = y1 + sc; *x2r = x2; *y2r = y2 + sc; } return found; } Eina_Bool termio_color_find(const Evas_Object *obj, int cx, int cy, int *x1r, int *y1r, int *x2r, int *y2r, uint8_t *rp, uint8_t *gp, uint8_t *bp, uint8_t *ap) { int x1, x2, y1, y2, w = 0, h = 0, sc; Eina_Bool goback = EINA_TRUE, goforward = EINA_FALSE; struct ty_sb sb = {.buf = NULL, .gap = 0, .len = 0, .alloc = 0}; Termpty *ty = termio_pty_get(obj); int res; char txt[8]; int txtlen = 0; Eina_Bool found = EINA_FALSE; int codepoint; uint8_t r, g, b, a = 255; EINA_SAFETY_ON_NULL_RETURN_VAL(ty, EINA_FALSE); x1 = x2 = cx; y1 = y2 = cy; termio_size_get(obj, &w, &h); if ((w <= 0) || (h <= 0)) goto end; sc = termio_scroll_get(obj); termpty_backlog_lock(); y1 -= sc; y2 -= sc; found = _sharp_color_find(ty, sc, &x1, &y1, &x2, &y2, &r, &g, &b, &a); if (found) goto end; res = _txt_at(ty, &x1, &y1, txt, &txtlen, &codepoint); if ((res != 0) || (txtlen == 0)) goto end; if (!_is_authorized_in_color(codepoint)) goto end; res = ty_sb_add(&sb, txt, txtlen); if (res < 0) goto end; while (goback) { int new_x1 = x1, new_y1 = y1; res = _txt_prev_at(ty, &new_x1, &new_y1, txt, &txtlen, &codepoint); if ((res != 0) || (txtlen == 0)) { goback = EINA_FALSE; goforward = EINA_TRUE; break; } res = ty_sb_prepend(&sb, txt, txtlen); if (res < 0) goto end; if (!_is_authorized_in_color(codepoint)) { ty_sb_lskip(&sb, txtlen); goback = EINA_FALSE; goforward = EINA_TRUE; break; } x1 = new_x1; y1 = new_y1; if (sbstartswith(&sb, "rgb") || sbstartswith(&sb, "hsl") || sbstartswith(&sb, "color")) { goback = EINA_FALSE; goforward = EINA_TRUE; break; } } while (goforward) { int new_x2 = x2, new_y2 = y2; /* Check if the previous char is a delimiter */ res = _txt_next_at(ty, &new_x2, &new_y2, txt, &txtlen, &codepoint); if ((res != 0) || (txtlen == 0) || (!_is_authorized_in_color(codepoint) && (codepoint != ')'))) break; res = ty_sb_add(&sb, txt, txtlen); if (res < 0) goto end; x2 = new_x2; y2 = new_y2; if (codepoint == ')') break; } /* colors do not span multiple lines (for the moment) */ if (y1 != y2) goto end; /* Trim */ while (sb.len && (sb.buf[0] == ' ' || sb.buf[0] == '\t')) { sb.len--; sb.buf++; sb.gap++; x1++; } while (sb.len && (sb.buf[sb.len-1] == ' ' || sb.buf[sb.len-1] == '\t')) { sb.len--; x2--; } if (!_parse_color(&sb, &r, &g, &b, &a)) goto end; found = EINA_TRUE; end: termpty_backlog_unlock(); ty_sb_free(&sb); if (found) { if (rp) *rp = r; if (gp) *gp = g; if (bp) *bp = b; if (ap) *ap = a; if (x1r) *x1r = x1; if (y1r) *y1r = y1 + sc; if (x2r) *x2r = x2; if (y2r) *y2r = y2 + sc; } return found; } #endif #if defined(BINARY_TYTEST) int tytest_color_parse_hex(void) { uint8_t v = 42; assert(_parse_hex('3', &v) == EINA_TRUE); assert(v == 3); assert(_parse_hex('b', &v) == EINA_TRUE); assert(v == 11); assert(_parse_hex('F', &v) == EINA_TRUE); assert(v == 15); assert(_parse_hex('@', &v) == EINA_FALSE); assert(_parse_hex('#', &v) == EINA_FALSE); assert(_parse_hex('G', &v) == EINA_FALSE); assert(_parse_hex('_', &v) == EINA_FALSE); assert(_parse_hex('g', &v) == EINA_FALSE); assert(_parse_hex('~', &v) == EINA_FALSE); return 0; } int tytest_color_parse_2hex(void) { uint8_t v = 42; assert(_parse_2hex("03", &v) == EINA_TRUE); assert(v == 3); assert(_parse_2hex("aF", &v) == EINA_TRUE); assert(v == 175); assert(_parse_2hex("44", &v) == EINA_TRUE); assert(v == 68); assert(_parse_2hex("gg", &v) == EINA_FALSE); assert(_parse_2hex("@3", &v) == EINA_FALSE); assert(_parse_2hex("#3", &v) == EINA_FALSE); assert(_parse_2hex("G3", &v) == EINA_FALSE); assert(_parse_2hex("_3", &v) == EINA_FALSE); assert(_parse_2hex("~3", &v) == EINA_FALSE); return 0; } int tytest_color_parse_sharp(void) { struct ty_sb sb = {}; uint8_t r = 0, g = 0, b = 0, a = 0; /* 3 letters */ assert(TY_SB_ADD(&sb, "#fab") == 0); assert(_parse_sharp_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 240 && g == 160 && b == 176 && a == 255); ty_sb_free(&sb); /* 4 letters */ assert(TY_SB_ADD(&sb, "#2345") == 0); assert(_parse_sharp_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 32 && g == 48 && b == 64 && a == 80); ty_sb_free(&sb); /* 6 letters */ assert(TY_SB_ADD(&sb, "#decfab") == 0); assert(_parse_sharp_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 222 && g == 207 && b == 171 && a == 255); ty_sb_free(&sb); /* 8 letters */ assert(TY_SB_ADD(&sb, "#fabdec86") == 0); assert(_parse_sharp_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 250 && g == 189 && b == 236 && a == 134); ty_sb_free(&sb); /* upper case */ assert(TY_SB_ADD(&sb, "#DECFAB") == 0); assert(_parse_sharp_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 222 && g == 207 && b == 171 && a == 255); ty_sb_free(&sb); /* mixed case */ assert(TY_SB_ADD(&sb, "#fAbDeC") == 0); assert(_parse_sharp_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 250 && g == 189 && b == 236 && a == 255); ty_sb_free(&sb); /* Invalid*/ /* improper start */ assert(TY_SB_ADD(&sb, "~decfab") == 0); assert(_parse_sharp_color(&sb, &r, &g, &b, &a) == EINA_FALSE); ty_sb_free(&sb); /* improper ending */ assert(TY_SB_ADD(&sb, "#decfab;") == 0); assert(_parse_sharp_color(&sb, &r, &g, &b, &a) == EINA_FALSE); ty_sb_free(&sb); /* invalid hex */ assert(TY_SB_ADD(&sb, "#dgc") == 0); assert(_parse_sharp_color(&sb, &r, &g, &b, &a) == EINA_FALSE); ty_sb_free(&sb); assert(TY_SB_ADD(&sb, "#dgca") == 0); assert(_parse_sharp_color(&sb, &r, &g, &b, &a) == EINA_FALSE); ty_sb_free(&sb); assert(TY_SB_ADD(&sb, "#dgcaca") == 0); assert(_parse_sharp_color(&sb, &r, &g, &b, &a) == EINA_FALSE); ty_sb_free(&sb); assert(TY_SB_ADD(&sb, "#dgcacaca") == 0); assert(_parse_sharp_color(&sb, &r, &g, &b, &a) == EINA_FALSE); ty_sb_free(&sb); assert(TY_SB_ADD(&sb, "#dgcacaca") == 0); assert(_parse_sharp_color(&sb, &r, &g, &b, &a) == EINA_FALSE); ty_sb_free(&sb); /* invalid length */ assert(TY_SB_ADD(&sb, "#a") == 0); assert(_parse_sharp_color(&sb, &r, &g, &b, &a) == EINA_FALSE); ty_sb_free(&sb); assert(TY_SB_ADD(&sb, "#da") == 0); assert(_parse_sharp_color(&sb, &r, &g, &b, &a) == EINA_FALSE); ty_sb_free(&sb); assert(TY_SB_ADD(&sb, "#dadad") == 0); assert(_parse_sharp_color(&sb, &r, &g, &b, &a) == EINA_FALSE); ty_sb_free(&sb); assert(TY_SB_ADD(&sb, "#dadadad") == 0); assert(_parse_sharp_color(&sb, &r, &g, &b, &a) == EINA_FALSE); ty_sb_free(&sb); assert(TY_SB_ADD(&sb, "#dadadadad") == 0); assert(_parse_sharp_color(&sb, &r, &g, &b, &a) == EINA_FALSE); ty_sb_free(&sb); return 0; } int tytest_color_parse_uint8(void) { struct ty_sb sb = {}; uint8_t v = 0; /* 1 digit (with/without ending) */ assert(TY_SB_ADD(&sb, "3 ") == 0); assert(_parse_uint8(&sb, &v) == EINA_TRUE); assert(v == 3); ty_sb_free(&sb); assert(TY_SB_ADD(&sb, "4") == 0); assert(_parse_uint8(&sb, &v) == EINA_TRUE); assert(v == 4); ty_sb_free(&sb); /* 2 digit (with/without ending) */ assert(TY_SB_ADD(&sb, "12 ") == 0); assert(_parse_uint8(&sb, &v) == EINA_TRUE); assert(v == 12); ty_sb_free(&sb); assert(TY_SB_ADD(&sb, "34") == 0); assert(_parse_uint8(&sb, &v) == EINA_TRUE); assert(v == 34); ty_sb_free(&sb); /* 3 digit (with/without ending) */ assert(TY_SB_ADD(&sb, "123 ") == 0); assert(_parse_uint8(&sb, &v) == EINA_TRUE); assert(v == 123); ty_sb_free(&sb); assert(TY_SB_ADD(&sb, "234") == 0); assert(_parse_uint8(&sb, &v) == EINA_TRUE); assert(v == 234); ty_sb_free(&sb); /* ending with semicolon */ assert(TY_SB_ADD(&sb, "42;") == 0); assert(_parse_uint8(&sb, &v) == EINA_TRUE); assert(v == 42); ty_sb_free(&sb); /* 0 padded */ assert(TY_SB_ADD(&sb, "012") == 0); assert(_parse_uint8(&sb, &v) == EINA_TRUE); assert(v == 12); ty_sb_free(&sb); assert(TY_SB_ADD(&sb, "007") == 0); assert(_parse_uint8(&sb, &v) == EINA_TRUE); assert(v == 7); ty_sb_free(&sb); /* too large */ assert(TY_SB_ADD(&sb, "256") == 0); assert(_parse_uint8(&sb, &v) == EINA_FALSE); ty_sb_free(&sb); /* too long */ assert(TY_SB_ADD(&sb, "1234") == 0); assert(_parse_uint8(&sb, &v) == EINA_FALSE); ty_sb_free(&sb); /* not a digit */ assert(TY_SB_ADD(&sb, "a") == 0); assert(_parse_uint8(&sb, &v) == EINA_FALSE); ty_sb_free(&sb); assert(TY_SB_ADD(&sb, ".") == 0); assert(_parse_uint8(&sb, &v) == EINA_FALSE); ty_sb_free(&sb); return 0; } int tytest_color_parse_edc(void) { struct ty_sb sb = {}; uint8_t r = 0, g = 0, b = 0, a = 0; /* color */ assert(TY_SB_ADD(&sb, " color: 51 153 255 32;") == 0); assert(_parse_edc_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 51 && g == 153 && b == 255 && a == 32); ty_sb_free(&sb); /* color2 */ assert(TY_SB_ADD(&sb, " color2: 244 99 93 127;") == 0); assert(_parse_edc_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 244 && g == 99 && b == 93 && a == 127); ty_sb_free(&sb); /* color3 */ assert(TY_SB_ADD(&sb, " color3: 149 181 16 234;") == 0); assert(_parse_edc_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 149 && g == 181 && b == 16 && a == 234); ty_sb_free(&sb); /* color: #color */ assert(TY_SB_ADD(&sb, " color: #3399ff20") == 0); assert(_parse_edc_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 51 && g == 153 && b == 255 && a == 32); ty_sb_free(&sb); /* with tabs */ assert(TY_SB_ADD(&sb, " color\t:\t244\t99\t93\t127\t;") == 0); assert(_parse_edc_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 244 && g == 99 && b == 93 && a == 127); ty_sb_free(&sb); /* invalid */ assert(TY_SB_ADD(&sb, " color: COL;") == 0); assert(_parse_edc_color(&sb, &r, &g, &b, &a) == EINA_FALSE); ty_sb_free(&sb); return 0; } int tytest_color_parse_css_rgb(void) { struct ty_sb sb = {}; uint8_t r = 0, g = 0, b = 0, a = 0; /* (rgb) Functional syntax */ assert(TY_SB_ADD(&sb, "rgb(255,1,153)") == 0); assert(_parse_css_rgb_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 255 && g == 1 && b == 153 && a == 255); ty_sb_free(&sb); assert(TY_SB_ADD(&sb, "rgb(254, 2, 152)") == 0); assert(_parse_css_rgb_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 254 && g == 2 && b == 152 && a == 255); ty_sb_free(&sb); assert(TY_SB_ADD(&sb, "rgb(253, 3, 151.0)") == 0); assert(_parse_css_rgb_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 253 && g == 3 && b == 151 && a == 255); ty_sb_free(&sb); /* (rgb) Percents */ assert(TY_SB_ADD(&sb, "rgb(100%,4%,40%)") == 0); assert(_parse_css_rgb_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 255 && g == 10 && b == 102 && a == 255); ty_sb_free(&sb); assert(TY_SB_ADD(&sb, "rgb(50%, 0%, 60%)") == 0); assert(_parse_css_rgb_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 127 && g == 0 && b == 153 && a == 255); ty_sb_free(&sb); /* (rgb) ERROR! Don't mix numbers and percentages. */ assert(TY_SB_ADD(&sb, "rgb(100%, 0, 60%)") == 0); assert(_parse_css_rgb_color(&sb, &r, &g, &b, &a) == EINA_FALSE); ty_sb_free(&sb); /* (rgb) Functional syntax with alpha value */ assert(TY_SB_ADD(&sb, "rgb(254, 1, 150, 0)") == 0); assert(_parse_css_rgb_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 254 && g == 1 && b == 150 && a == 0); ty_sb_free(&sb); assert(TY_SB_ADD(&sb, "rgb(253, 2, 149, 1)") == 0); assert(_parse_css_rgb_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 253 && g == 2 && b == 149 && a == 255); ty_sb_free(&sb); assert(TY_SB_ADD(&sb, "rgb(252, 3, 148, 50%)") == 0); assert(_parse_css_rgb_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 252 && g == 3 && b == 148 && a == 128); ty_sb_free(&sb); /* (rgb) Whitespace syntax */ assert(TY_SB_ADD(&sb, "rgb(255 0 153)") == 0); assert(_parse_css_rgb_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 255 && g == 0 && b == 153 && a == 255); ty_sb_free(&sb); assert(TY_SB_ADD(&sb, "rgb(254 1 152 / 0)") == 0); assert(_parse_css_rgb_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 254 && g == 1 && b == 152 && a == 0); ty_sb_free(&sb); assert(TY_SB_ADD(&sb, "rgb(253 2 151 / 100%)") == 0); assert(_parse_css_rgb_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 253 && g == 2 && b == 151 && a == 255); ty_sb_free(&sb); /* (rgb) Functional syntax with floats value */ assert(TY_SB_ADD(&sb, "rgb(255, 0, 153.6, 1)") == 0); assert(_parse_css_rgb_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 255 && g == 0 && b == 154 && a == 255); ty_sb_free(&sb); assert(TY_SB_ADD(&sb, "rgb(1e2, .5e1, .5e0, +.25e2%)") == 0); assert(_parse_css_rgb_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 100 && g == 5 && b == 1 && a == 64); ty_sb_free(&sb); /* (rgba) Functional syntax */ assert(TY_SB_ADD(&sb, "rgba(51, 170, 51, .1)") == 0); /* 10% opaque green */ assert(_parse_css_rgb_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 51 && g == 170 && b == 51 && a == 26); ty_sb_free(&sb); assert(TY_SB_ADD(&sb, "rgba(50, 171, 52, .4)") == 0); /* 40% opaque green */ assert(_parse_css_rgb_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 50 && g == 171 && b == 52 && a == 102); ty_sb_free(&sb); assert(TY_SB_ADD(&sb, "rgba(49, 172, 53, .7)") == 0); /* 70% opaque green */ assert(_parse_css_rgb_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 49 && g == 172 && b == 53 && a == 179); ty_sb_free(&sb); assert(TY_SB_ADD(&sb, "rgba(48, 173, 54, 1)") == 0); /* full opaque green */ assert(_parse_css_rgb_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 48 && g == 173 && b == 54 && a == 255); ty_sb_free(&sb); /* (rgba) Whitespace syntax */ assert(TY_SB_ADD(&sb, "rgba(51 170 51 / 0.4)") == 0); /* 40% opaque green */ assert(_parse_css_rgb_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 51 && g == 170 && b == 51 && a == 102); ty_sb_free(&sb); assert(TY_SB_ADD(&sb, "rgba(50 171 50 / 70%)") == 0); /* 40% opaque green */ assert(_parse_css_rgb_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 50 && g == 171 && b == 50 && a == 179); ty_sb_free(&sb); /* (rgba) ERROR! invalid alpha */ assert(TY_SB_ADD(&sb, "rgba(51 170 51 / 40)") == 0); assert(_parse_css_rgb_color(&sb, &r, &g, &b, &a) == EINA_FALSE); ty_sb_free(&sb); /* (rgba) Functional syntax with floats value */ assert(TY_SB_ADD(&sb, "rgba(255, 0, 153.6, 1)") == 0); assert(_parse_css_rgb_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 255 && g == 0 && b == 154 && a == 255); ty_sb_free(&sb); assert(TY_SB_ADD(&sb, "rgba(1e2, .5e1, .5e0, +.25e2%)") == 0); assert(_parse_css_rgb_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 100 && g == 5 && b == 1 && a == 64); ty_sb_free(&sb); return 0; } int tytest_color_parse_css_hsl(void) { struct ty_sb sb = {}; uint8_t r = 0, g = 0, b = 0, a = 0; /* These examples all specify the same color: a lavender. */ assert(TY_SB_ADD(&sb, "hsl(270,60%,70%)") == 0); assert(_parse_css_hsl_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 178 && g == 132 && b == 224 && a == 255); ty_sb_free(&sb); r = g = b = a = 0; assert(TY_SB_ADD(&sb, "hsl(270, 60%, 70%)") == 0); assert(_parse_css_hsl_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 178 && g == 132 && b == 224 && a == 255); ty_sb_free(&sb); r = g = b = a = 0; assert(TY_SB_ADD(&sb, "hsl(270 60% 70%)") == 0); assert(_parse_css_hsl_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 178 && g == 132 && b == 224 && a == 255); ty_sb_free(&sb); r = g = b = a = 0; assert(TY_SB_ADD(&sb, "hsl(270deg, 60%, 70%)") == 0); assert(_parse_css_hsl_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 178 && g == 132 && b == 224 && a == 255); ty_sb_free(&sb); r = g = b = a = 0; assert(TY_SB_ADD(&sb, "hsl(4.71239rad, 60%, 70%)") == 0); assert(_parse_css_hsl_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 178 && g == 132 && b == 224 && a == 255); ty_sb_free(&sb); r = g = b = a = 0; assert(TY_SB_ADD(&sb, "hsl(300grad, 60%, 70%)") == 0); assert(_parse_css_hsl_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 178 && g == 132 && b == 224 && a == 255); ty_sb_free(&sb); r = g = b = a = 0; assert(TY_SB_ADD(&sb, "hsl(.75turn, 60%, 70%)") == 0); assert(_parse_css_hsl_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 178 && g == 132 && b == 224 && a == 255); ty_sb_free(&sb); r = g = b = a = 0; /* These examples all specify the same color: a lavender that is 15% opaque. */ assert(TY_SB_ADD(&sb, "hsl(270, 60%, 50%, .15)") == 0); assert(_parse_css_hsl_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 127 && g == 51 && b == 204 && a == 38); ty_sb_free(&sb); r = g = b = a = 0; assert(TY_SB_ADD(&sb, "hsl(270, 60%, 50%, 15%)") == 0); assert(_parse_css_hsl_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 127 && g == 51 && b == 204 && a == 38); ty_sb_free(&sb); r = g = b = a = 0; assert(TY_SB_ADD(&sb, "hsl(270 60% 50% / .15)") == 0); assert(_parse_css_hsl_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 127 && g == 51 && b == 204 && a == 38); ty_sb_free(&sb); r = g = b = a = 0; assert(TY_SB_ADD(&sb, "hsl(270 60% 50% / 15%)") == 0); assert(_parse_css_hsl_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 127 && g == 51 && b == 204 && a == 38); ty_sb_free(&sb); r = g = b = a = 0; /* Different shades */ assert(TY_SB_ADD(&sb, "hsla(240, 100%, 50%, .05)") == 0); assert(_parse_css_hsl_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 0 && g == 0 && b == 255 && a == 13); ty_sb_free(&sb); r = g = b = a = 0; assert(TY_SB_ADD(&sb, "hsla(240, 100%, 50%, .4)") == 0); assert(_parse_css_hsl_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 0 && g == 0 && b == 255 && a == 102); ty_sb_free(&sb); r = g = b = a = 0; assert(TY_SB_ADD(&sb, "hsla(240, 100%, 50%, .7)") == 0); assert(_parse_css_hsl_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 0 && g == 0 && b == 255 && a == 179); ty_sb_free(&sb); r = g = b = a = 0; assert(TY_SB_ADD(&sb, "hsla(240, 100%, 50%, 1)") == 0); assert(_parse_css_hsl_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 0 && g == 0 && b == 255 && a == 255); ty_sb_free(&sb); r = g = b = a = 0; /* Whitespace syntax */ assert(TY_SB_ADD(&sb, "hsla(240 100% 50% / .05)") == 0); assert(_parse_css_hsl_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 0 && g == 0 && b == 255 && a == 13); ty_sb_free(&sb); r = g = b = a = 0; /* Percentage value for alpha */ assert(TY_SB_ADD(&sb, "hsla(240 100% 50% / 5%)") == 0); assert(_parse_css_hsl_color(&sb, &r, &g, &b, &a) == EINA_TRUE); assert(r == 0 && g == 0 && b == 255 && a == 13); ty_sb_free(&sb); r = g = b = a = 0; return 0; } #endif #if defined(BINARY_TYFUZZ_COLOR_PARSER) int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { struct ty_sb sb = {}; uint8_t r, g, b, a; assert(!ty_sb_add(&sb, (const char*)data, size)); _parse_color(&sb, &r, &g, &b, &a); ty_sb_free(&sb); return 0; } #endif